aboutsummaryrefslogtreecommitdiffstats
path: root/libccnx-transport-rta/ccnx
diff options
context:
space:
mode:
Diffstat (limited to 'libccnx-transport-rta/ccnx')
-rw-r--r--libccnx-transport-rta/ccnx/CMakeLists.txt4
-rw-r--r--libccnx-transport-rta/ccnx/api/CMakeLists.txt2
-rw-r--r--libccnx-transport-rta/ccnx/api/control/CMakeLists.txt91
-rw-r--r--libccnx-transport-rta/ccnx/api/control/README.txt274
-rw-r--r--libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_About.c44
-rw-r--r--libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_About.h54
-rw-r--r--libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.c449
-rw-r--r--libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.h456
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Acks.c133
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Acks.h149
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Address.c502
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Address.h599
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_AddressList.c206
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_AddressList.h222
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.c100
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.h108
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Connection.c308
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Connection.h320
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.c294
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.h281
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.c160
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.h185
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.c159
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.h202
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.c245
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.h587
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.c173
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.h242
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.c158
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.h53
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Interface.c255
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Interface.h236
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.c183
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.h243
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.c127
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.h219
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPMulticast.h179
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.c331
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.h349
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.c159
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.h204
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceL2Group.h179
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceLocal.h143
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.c210
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.h232
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.c102
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.h98
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Listener.c432
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_Listener.h427
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.c103
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.h35
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.c188
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.h248
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.c167
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.h49
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.c59
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.h95
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.c64
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.h92
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.c466
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.h587
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.c163
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.h166
-rw-r--r--libccnx-transport-rta/ccnx/api/control/cpi_private.h60
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/.gitignore24
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/CMakeLists.txt36
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_controlPlaneInterface.c287
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Acks.c119
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Address.c550
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_AddressList.c317
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_CancelFlow.c115
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Connection.c220
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionEthernet.c290
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionList.c186
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlFacade.c220
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlMessage.c378
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Forwarding.c331
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Interface.c351
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceEthernet.c191
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceGeneric.c157
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnel.c224
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnelList.c181
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceSet.c243
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceTypes.c91
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Listener.c384
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_ManageLinks.c266
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_NameRouteType.c93
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_Registration.c72
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntry.c547
-rw-r--r--libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntryList.c177
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/CMakeLists.txt39
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/README20
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_About.c44
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_About.h54
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/notify_Status.c211
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/notify_Status.h345
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/notify_Timer.h18
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/test/.gitignore1
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/test/CMakeLists.txt13
-rw-r--r--libccnx-transport-rta/ccnx/api/notify/test/test_notify_Status.c96
-rw-r--r--libccnx-transport-rta/ccnx/config.h.in4
-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
275 files changed, 55595 insertions, 0 deletions
diff --git a/libccnx-transport-rta/ccnx/CMakeLists.txt b/libccnx-transport-rta/ccnx/CMakeLists.txt
new file mode 100644
index 00000000..997b4f54
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/CMakeLists.txt
@@ -0,0 +1,4 @@
+configure_file(config.h.in config.h @ONLY)
+
+add_subdirectory(api)
+add_subdirectory(transport)
diff --git a/libccnx-transport-rta/ccnx/api/CMakeLists.txt b/libccnx-transport-rta/ccnx/api/CMakeLists.txt
new file mode 100644
index 00000000..971a364e
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_subdirectory(control)
+add_subdirectory(notify)
diff --git a/libccnx-transport-rta/ccnx/api/control/CMakeLists.txt b/libccnx-transport-rta/ccnx/api/control/CMakeLists.txt
new file mode 100644
index 00000000..b0b3e71a
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/CMakeLists.txt
@@ -0,0 +1,91 @@
+# Define a few configuration variables that we want accessible in the software
+
+set(CCNX_API_CONTROL_HEADERS
+ ccnxControlAPI_About.h
+ cpi_Acks.h
+ cpi_Address.h
+ cpi_AddressList.h
+ cpi_CancelFlow.h
+ cpi_Connection.h
+ cpi_ConnectionEthernet.h
+ cpi_ConnectionList.h
+ cpi_ControlMessage.h
+ cpi_ControlFacade.h
+ cpi_Forwarding.h
+ cpi_Interface.h
+ cpi_InterfaceSet.h
+ cpi_InterfaceGeneric.h
+ cpi_InterfaceEthernet.h
+ cpi_InterfaceType.h
+ cpi_InterfaceIPTunnel.h
+ cpi_InterfaceIPTunnelList.h
+ cpi_InterfaceLocal.h
+ cpi_Listener.h
+ cpi_NameRouteType.h
+ cpi_ManageLinks.h
+ cpi_ManageCaches.h
+ cpi_ManageWldr.h
+ cpi_RouteEntry.h
+ cpi_RouteEntryList.h
+ cpi_NameRouteProtocolType.h
+ cpi_ForwardingStrategy.h
+ controlPlaneInterface.h
+)
+
+set(CCNX_API_CONTROL_SOURCE_FILES
+ ccnxControlAPI_About.c
+ cpi_Acks.c
+ cpi_Address.c
+ cpi_AddressList.c
+ cpi_CancelFlow.c
+ cpi_Connection.c
+ cpi_ConnectionEthernet.c
+ cpi_ConnectionList.c
+ cpi_ControlMessage.c
+ cpi_ControlFacade.c
+ cpi_Forwarding.c
+ cpi_Interface.c
+ cpi_InterfaceSet.c
+ cpi_InterfaceGeneric.c
+ cpi_InterfaceEthernet.c
+ cpi_InterfaceIPTunnel.c
+ cpi_InterfaceIPTunnelList.c
+ cpi_InterfaceType.c
+ cpi_Listener.c
+ cpi_NameRouteType.c
+ cpi_ManageLinks.c
+ cpi_ManageCaches.c
+ cpi_ManageWldr.c
+ cpi_NameRouteProtocolType.c
+ cpi_RouteEntry.c
+ cpi_RouteEntryList.c
+ cpi_ForwardingStrategy.c
+ controlPlaneInterface.c
+)
+
+
+add_library(ccnx_api_control STATIC ${CCNX_API_CONTROL_SOURCE_FILES} ${CCNX_API_CONTROL_HEADERS})
+add_library(ccnx_api_control.shared SHARED ${CCNX_API_CONTROL_SOURCE_FILES})
+
+source_group(Sources FILES ${CCNX_API_CONTROL_SOURCE_FILES})
+source_group(Sources FILES ${CCNX_API_CONTROL_HEADERS})
+
+set_target_properties(ccnx_api_control.shared PROPERTIES
+ C_STANDARD 99
+ SOVERSION 1
+ VERSION 1.0
+ OUTPUT_NAME ccnx_api_control )
+
+set(libccnx_api_control_libraries
+ ccnx_api_control
+ ccnx_api_control.shared
+ )
+
+foreach(lib ${libccnx_api_control_libraries})
+ install(TARGETS ${lib} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
+ set_property(TARGET ${lib} PROPERTY C_STANDARD 99)
+endforeach()
+
+install(FILES ${CCNX_API_CONTROL_HEADERS} DESTINATION include/ccnx/api/control )
+
+add_subdirectory(test)
diff --git a/libccnx-transport-rta/ccnx/api/control/README.txt b/libccnx-transport-rta/ccnx/api/control/README.txt
new file mode 100644
index 00000000..291a2088
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/README.txt
@@ -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.
+ */
+
+Control Plane Interface (CPI)
+
+Control's the FIB and forwarder interfaces.
+
+Also controls stack behavior, such as flushing the stack.
+
+These messages are meant to be sent up and down transport connections, not
+over the command port.
+
+The matieral below describes the networking model used by the Transport.
+
+=====================================
+
+Most of the API functions are found in cpi_ManageLinks.h and cpi_Forwarding.h. Those
+have the calls to generate CPIControlMessages for dealing with interfaces and dealing with
+forwarding.
+
+=====================================
+0) Overview
+
+To use the ControlPlaneInterface, you create CPIControlMessages, then send them
+down a Transport connection to a forwarder. The Forwarder Connector, in conjunction
+with the forwarder will send a CPIControlMessage back up the stack to you. The
+Response might be an ACK, a NACK, or a Respone with data.
+
+Different Transports and different forwarders will have their own models of
+ports, interfaces, and addresses. It is the job of the Transport and the
+Forwarder Connector to map those different views in to the single consistent
+view exposed here to the API.
+
+All addressable entities have an "Interface Index" (ifidx), similar to
+RFC 3493, Section 4. The interface index used by the Transport includes
+virtual interfaces that may not exist at the kernel level, so the ifidx
+is not equal to a kernel interface index. For some devices, the CCNx
+forwarder might not even be on the same physical entity.
+
+Not all forwarders support all options. See forwarder specific documentation.
+
+=====================================
+1) Interfaces
+
+An interface is a physical port or virtual device on the system.
+It may have zero or more addresses, depending on the type.
+
+- P2P interface (e.g. serial)
+- Ethernet
+- Loopback
+
+Some interfaces depend on other interfaces:
+
+- VLAN
+- L2TP
+- PPP
+- LAG
+
+Interfaces will have a layer 2 (L2) address and may have L3 addresses too.
+
+All physical and virtual interfaces have an interface index.
+
+=====================================
+2) Overlay Interfaces (Tunnels and Multicast groups)
+
+Tunnels are a special type of Interface that have a point-to-point
+connection with a remote peer. Another type of overlay is
+an IP multicast group.
+
+Tunnels have a specific local source address. Each tunnel and multicast
+group overlay has an interface index.
+
+Type types of interaces are:
+- IP/UDP point-to-point tunnel
+- IPv6/UDP point-to-point tunnel
+- IP/TCP point-to-point tunnel
+- IPv6/TCP point-to-point tunnel
+- IP/UDP multicast group
+- IPv6/UDP multicast group
+
+
+=====================================
+3) Addresses
+
+Addresses are used for setting up tunnels and overlays. They
+are also used in the FIB to indicate a next hop.
+
+An Interface address may be used with tunnels and overlays to
+indicate that a message (i.e. interest) should be sent on that
+overlay.
+
+- IPv4 unicast
+- IPv4 multicast
+- IPv6 unicast
+- IPv6 multicast
+- Link unicast
+- Link group
+- Interface
+
+=====================================
+4) Message information
+
+Similar to RFC 3542, Section 6
+
+An outgoing message can specify:
+- The outgoing interface index
+- The outgoing hop limit
+
+If the outgoing interface is specified, it bypasses the normal
+FIB/PIT forwarding rules and forces the message out that interface.
+
+
+Incoming message information may contain:
+- The destination address
+- The arriving interface index
+- The arriving hop limit
+
+There is currently no specification for traffic class.
+
+The destination address will be specific to how the message was
+received. If it was over an IP-based interface, the addresses will
+be IP/IPv6. If it was over a Link interface, the addresses wil be
+media-dependent addresses (e.g. Ethernet MACs).
+
+To control the incoming information, use these functions
+to generate a control message to send down the connection.
+They are similar to an IP(V6)_RECVPKTINFO socket option.
+
+CPIControlMessage * cpi_StartReceivingMessageInfo();
+CPIControlMessage * cpi_StopReceivingMessageInfo();
+
+DESCRIBE HOW IT IS COMMUNICATED IN A CCNxMESSAGE
+
+=====================================
+5) Monitors
+
+A Transport connection can be setup as a Monitor. A Montior is
+an INBOUND ONLY connection for diagnostic purposes. It should
+contain only the minimum necessary components (API, CODEC, Forwarder).
+
+A Monitor has directionality. It may snoop all messages INBOUND or OUTBOUND
+or BOTH on the target. The snooped messages are sent up the monitor
+connection to the API.
+
+A Monitor may be added to an Interface, in which case it will act like
+a promiscuous tap. An INBOUND monitor will see all packets received
+on the interface. An OUTBOUND monitor will see all packets sent on the
+interface.
+
+A Monitor may be added to a namespace. It can be for an exact namespace,
+or it can be a prefix match. It may be INBOUND or OUTBOUND or BOTH.
+
+=====================================
+Common Operations
+
+####
+#### NOTE: This example code is out of date.
+####
+
+----------------------------------------------------------------------------
+a) List the interfaces
+
+CPIControlMessage * cpi_NetworkInterfaceList();
+/* send the control message on a connection */
+
+CPIControlMessage *message = /* receive function */
+if( cpi_IsCpiMessage(message) && cpi_GetMessageType(message) == CPI_RESPONSE &&
+ cpi_GetMessageOperation(message) == CPI_INTERFACE_LIST )
+{
+ unsigned count = cpi_NetworkInterfaceList_Count(message);
+ for(i = 0; i < count; i++)
+ {
+ InterfaceService *entry = cpi_NetworkInterfaceList_Get(message, i);
+ }
+}
+
+ccnxControlMessage_Destroy(&message);
+
+
+----------------------------------------------------------------------------
+b) Create a point-to-point tunnel
+
+CPIAddress * dest = cpiAddress_CreateFromInet((struct sockaddr_in) {
+ .sa_addr = inet_addr("foo.bar.com"),
+ .sa_port = htons(9695)} );
+
+// the address 13.0.1.1. is known to be on the forwarder.
+// You'd learn about it from cpi_NetworkInterfaceList()
+CPIAddress * source = cpiAddress_CreateFromInet((struct sockaddr_in) {
+ .sa_family = AF_INET,
+ .sa_addr = inet_addr("13.0.1.1") } );
+
+CPIControlMessage *udp_tunnel = cpiTunnel_CreateMessage(dest, source, IPPROTO_UDP)
+/* send message down connection */
+
+CPIControlMessage *response = /* receive message function */
+
+if( cpi_IsCpiMessage(response) && cpi_GetMessageType(response) == CPI_RESPONSE &&
+ cpi_GetMessageOperation(response) == CPI_CREATE_TUNNEL )
+{
+ CPITunnel * tunnel = cpiTunnel_ParseMessage(response);
+
+ unsigned ifidx = cpiTunnel_GetInterfaceIndex(tunnel);
+ // ...
+ cpiTunnel_Destroy(&tunnel);
+}
+
+
+
+----------------------------------------------------------------------------
+c) Create an IP multicast overlay
+
+CPIAddress * dest = cpiAddress_CreateFromInet((struct sockaddr_in) {
+ .sa_family = AF_INET,
+ .sa_addr = inet_addr("224.1.100.3"),
+ .sa_port = htons(9695)} );
+
+// the address 13.0.1.1. is known to be on the forwarder.
+// You'd learn about it from cpi_NetworkInterfaceList()
+CPIAddress * source = cpiAddress_CreateFromInet((struct sockaddr_in) {
+ .sa_family = AF_INET,
+ .sa_addr = inet_addr("13.0.1.1") } );
+
+unsigned ifidx = /* interface to join the group */
+
+CPIControlMessage *udp_multicast = cpiMulticast_CreateMessage(dest, source, ifidx)
+/* send message down connection */
+
+CPIControlMessage *response = /* receive message function */
+
+if( cpi_IsCpiMessage(response) && cpi_GetMessageType(response) == CPI_RESPONSE &&
+ cpi_GetMessageOperation(response) == CPI_CREATE_MULTICAST )
+{
+ CPIMulticast * mcast = cpiMulticast_ParseMessage(response);
+ // ...
+ cpiMulticast_Destroy(&tunnel);
+}
+
+----------------------------------------------------------------------------
+d) Create an Ethernet group interface
+
+
+
+
+----------------------------------------------------------------------------
+d) remove a tunnel
+
+----------------------------------------------------------------------------
+d) Setup a FIB entry to a point-to-point tuennl
+
+----------------------------------------------------------------------------
+e) Setup a FIB entry to a multicast group
+
+----------------------------------------------------------------------------
+f) Setup a FIB entry to a Link unicast address
+
+----------------------------------------------------------------------------
+g) Setup a FIB entry to a Link group address
+
+
+
+
diff --git a/libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_About.c b/libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_About.c
new file mode 100644
index 00000000..51b09086
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_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 "ccnxControlAPI_About.h"
+
+const char *ccnxControlAPI_What = "@(#)" "ccnxControlAPI " RELEASE_VERSION " 2017-02-20T14:20:58.851661"
+ "@(#)" "\tCopyright (c) 2017 Cisco and/or its affiliates.";
+
+const char *
+ccnxControlAPIAbout_Name(void)
+{
+ return "ccnxControlAPI";
+}
+
+const char *
+ccnxControlAPIAbout_Version(void)
+{
+ return RELEASE_VERSION;
+}
+
+const char *
+ccnxControlAPIAbout_About(void)
+{
+ return "ccnxControlAPI "RELEASE_VERSION " 2017-02-20T14:20:58.851661" "\nCopyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxControlAPIAbout_MiniNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxControlAPIAbout_ShortNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxControlAPIAbout_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/api/control/ccnxControlAPI_About.h b/libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_About.h
new file mode 100644
index 00000000..ee04c843
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/ccnxControlAPI_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 ccnxControlAPI_About_h
+#define ccnxControlAPI_About_h
+/**
+ * Embedded string containing information for the what(1) command.
+ *
+ */
+extern const char *ccnxControlAPI_What;
+
+/**
+ * Return the name as a C string.
+ *
+ * @return The name as a C string.
+ */
+const char *ccnxControlAPIAbout_Name(void);
+
+/**
+ * Return the version as a C string.
+ *
+ * @return The version as a C string.
+ */
+const char *ccnxControlAPIAbout_Version(void);
+
+/**
+ * Return the About text as a C string.
+ *
+ * @return The About text as a C string.
+ */
+const char *ccnxControlAPIAbout_About(void);
+
+/**
+ * Return the minimum copyright notice as a C string.
+ *
+ * @return The minimum copyright notice as a C string.
+ */
+const char *ccnxControlAPIAbout_MiniNotice(void);
+
+/**
+ * Return the short copyright notice as a C string.
+ *
+ * @return The short copyright notice as a C string.
+ */
+const char *ccnxControlAPIAbout_ShortNotice(void);
+
+/**
+ * Return the long copyright notice as a C string.
+ *
+ * @return The long copyright notice as a C string.
+ */
+const char *ccnxControlAPIAbout_LongNotice(void);
+
+#endif // ccnxControlAPI_About_h
diff --git a/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.c b/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.c
new file mode 100644
index 00000000..469acd02
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.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.
+ */
+
+/**
+ * @header <#Headline Name#>
+ * <#Abstract#>
+ *
+ * <#Discussion#>
+ *
+ * @author Marc Mosko
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+
+/**
+ * These comments describe the implementation of the protocol described in the header file.
+ *
+ * A Control Plane Information (CPI) message is a JSON object of this form:
+ * {
+ * "CPI_REQUEST" | "CPI_RESPONSE" :
+ * { "SEQUENCE" : <sequence number>,
+ * <operation> : <contents>
+ * }
+ * ["AUTHENTICATOR" : <TBD proof based on request/response, e.g. a crypto signature>]
+ * }
+ *
+ * {
+ * "CPI_ACK" :
+ * { "SEQUENCE" : <sequence number>,
+ * "RETURN" : "ACK" or "NACK",
+ * "REQUEST" : <original request JSON>
+ * [, "MESSAGE" : <optional message> ]
+ * }
+ * ["AUTHENTICATOR" : <TBD proof based on request/response, e.g. a crypto signature>]
+ * }
+ *
+ *
+ * { "REGISTER" :
+ * { "PREFIX" : <name URI string>,
+ * "INTERFACE" : <integer>,
+ * "FLAGS" : <integer>
+ * [, "LIFETIME" : [seconds, micro_seconds] ]
+ * }
+ * }
+ *
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+
+#include <LongBow/runtime.h>
+
+#include "controlPlaneInterface.h"
+#include "cpi_private.h"
+#include "cpi_NameRouteProtocolType.h"
+#include "cpi_Acks.h"
+#include <ccnx/api/control/cpi_ConnectionEthernet.h>
+
+static const char *cpiRequest = "CPI_REQUEST";
+static const char *cpiResponse = "CPI_RESPONSE";
+static const char *cpiPause = "CPI_PAUSE";
+static const char *cpiFlush = "CPI_FLUSH";
+
+// This is the unique sequence number used by all messages and its thread locks
+static pthread_mutex_t cpiNextSequenceNumberMutex = PTHREAD_MUTEX_INITIALIZER;
+static uint64_t cpiNextSequenceNumber = 1;
+
+const char *
+cpiRequest_GetJsonTag()
+{
+ return cpiRequest;
+}
+
+const char *
+cpiResponse_GetJsonTag()
+{
+ return cpiResponse;
+}
+
+const char *
+cpiSequence_GetJSONTag()
+{
+ return cpiSeqnum;
+}
+
+uint64_t
+cpi_GetNextSequenceNumber(void)
+{
+ uint64_t seqnum;
+
+ int result = pthread_mutex_lock(&cpiNextSequenceNumberMutex);
+ assertTrue(result == 0, "Got error from pthread_mutex_lock: %d", result);
+
+ seqnum = cpiNextSequenceNumber++;
+
+ result = pthread_mutex_unlock(&cpiNextSequenceNumberMutex);
+ assertTrue(result == 0, "Got error from pthread_mutex_unlock: %d", result);
+
+ return seqnum;
+}
+
+CpiOperation
+cpi_getCPIOperation2(const PARCJSON *json)
+{
+ PARCJSONValue *cpi_value = parcJSON_GetValueByName(json, cpiRequest);
+
+ if (cpi_value == NULL) {
+ cpi_value = parcJSON_GetValueByName(json, cpiResponse);
+ }
+ assertNotNull(cpi_value, "Could not get Request or response");
+
+ PARCJSON *cpi_json = parcJSONValue_GetJSON(cpi_value);
+
+ /*
+ * The JSON is defined as { REQUEST : { SEQUENCE: xxx, <OPERATION>: xxx } }
+ * so we want to get the key of the 2nd item (index 1) of the array of objects
+ * under the request
+ */
+
+ PARCJSONPair *item1Pair = parcJSON_GetPairByIndex(cpi_json, 1);
+ PARCBuffer *name = parcJSONPair_GetName(item1Pair);
+ const char *p = parcBuffer_Overlay(name, 0);
+
+ if (strncasecmp(p, cpiForwarding_AddRouteJsonTag(), strlen(cpiForwarding_AddRouteJsonTag())) == 0) {
+ return CPI_REGISTER_PREFIX;
+ }
+
+ if (strncasecmp(p, cpiForwarding_RemoveRouteJsonTag(), strlen(cpiForwarding_RemoveRouteJsonTag())) == 0) {
+ return CPI_UNREGISTER_PREFIX;
+ }
+
+ if (strncasecmp(p, cpiPause, strlen(cpiPause)) == 0) {
+ return CPI_PAUSE;
+ }
+
+ if (strncasecmp(p, cpiFlush, strlen(cpiFlush)) == 0) {
+ return CPI_FLUSH;
+ }
+
+ if (strncasecmp(p, cpiCancelFlow_CancelFlowJsonTag(), strlen(cpiCancelFlow_CancelFlowJsonTag())) == 0) {
+ return CPI_CANCEL_FLOW;
+ }
+
+ if (strncasecmp(p, cpiLinks_InterfaceListJsonTag(), strlen(cpiLinks_InterfaceListJsonTag())) == 0) {
+ return CPI_INTERFACE_LIST;
+ }
+
+ if (strncasecmp(p, cpiForwarding_RouteListJsonTag(), strlen(cpiForwarding_RouteListJsonTag())) == 0) {
+ return CPI_PREFIX_REGISTRATION_LIST;
+ }
+
+ if (strncasecmp(p, cpiLinks_CreateTunnelJsonTag(), strlen(cpiLinks_CreateTunnelJsonTag())) == 0) {
+ return CPI_CREATE_TUNNEL;
+ }
+
+ if (strncasecmp(p, cpiLinks_RemoveTunnelJsonTag(), strlen(cpiLinks_RemoveTunnelJsonTag())) == 0) {
+ return CPI_REMOVE_TUNNEL;
+ }
+
+ if (strncasecmp(p, cpiLinks_ConnectionListJsonTag(), strlen(cpiLinks_ConnectionListJsonTag())) == 0) {
+ return CPI_CONNECTION_LIST;
+ }
+
+ if (strncasecmp(p, cpiLinks_AddEtherConnectionJasonTag(), strlen(cpiLinks_AddEtherConnectionJasonTag())) == 0) {
+ return (CPI_ADD_ETHER_CONNECTION);
+ }
+
+ if (strncasecmp(p, cpiManageChaces_CacheStoreOnJsonTag(), strlen(cpiManageChaces_CacheStoreOnJsonTag())) == 0) {
+ return CPI_CACHE_STORE_ON;
+ }
+
+ if (strncasecmp(p, cpiManageChaces_CacheStoreOffJsonTag(), strlen(cpiManageChaces_CacheStoreOffJsonTag())) == 0) {
+ return CPI_CACHE_STORE_OFF;
+ }
+
+ if (strncasecmp(p, cpiManageChaces_CacheServeOnJsonTag(), strlen(cpiManageChaces_CacheServeOnJsonTag())) == 0) {
+ return CPI_CACHE_SERVE_ON;
+ }
+
+ if (strncasecmp(p, cpiManageChaces_CacheServeOffJsonTag(), strlen(cpiManageChaces_CacheServeOffJsonTag())) == 0) {
+ return CPI_CACHE_SERVE_OFF;
+ }
+
+ if (strncasecmp(p, cpiManageChaces_CacheClearJsonTag(), strlen(cpiManageChaces_CacheClearJsonTag())) == 0) {
+ return CPI_CACHE_CLEAR;
+ }
+
+ if (strncasecmp(p, cpiForwarding_SetStrategyJsonTag(), strlen(cpiForwarding_SetStrategyJsonTag())) == 0) {
+ return CPI_SET_FORWARDING_STRATEGY;
+ }
+
+ if (strncasecmp(p, cpiLinks_SetWldrJsonTag(), strlen(cpiLinks_SetWldrJsonTag())) == 0) {
+ return CPI_SET_WLDR;
+ }
+
+ if (strncasecmp(p, "AddConnEther", strlen("AddConnEther")) == 0) {
+ return CPI_ADD_CONNECTION_ETHERNET;
+ }
+
+ if (strncasecmp(p, "RemoveConnEther", strlen("RemoveConnEther")) == 0) {
+ return CPI_REMOVE_CONNECTION_ETHERNET;
+ }
+
+ if (strncasecmp(p, "AddListener", strlen("AddListener")) == 0) {
+ return CPI_ADD_LISTENER;
+ }
+
+ if (strncasecmp(p, "RemoveListener", strlen("RemoveListener")) == 0) {
+ return CPI_REMOVE_LISTENER;
+ }
+
+ trapIllegalValue(json, "Could not parse: %s\n", parcJSON_ToString(json));
+}
+
+/**
+ * Return the relevant operation from a REQUEST or a REPSONSE.
+ * Do not call on an ACK
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CpiOperation
+cpi_GetMessageOperation(CCNxControl *control)
+{
+ if (cpiConnectionEthernet_IsAddMessage(control)) {
+ return CPI_ADD_CONNECTION_ETHERNET;
+ }
+
+ if (cpiConnectionEthernet_IsRemoveMessage(control)) {
+ return CPI_REMOVE_CONNECTION_ETHERNET;
+ }
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+
+ CpiOperation result = cpi_getCPIOperation2(json);
+ return result;
+}
+
+CpiMessageType
+controlPlaneInterface_GetCPIMessageType(PARCJSON *json)
+{
+ assertNotNull(json, "Invalid state, got NULL json from control message");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiResponse);
+ if (value != NULL) {
+ return CPI_RESPONSE;
+ }
+
+ value = parcJSON_GetValueByName(json, cpiRequest);
+ if (value != NULL) {
+ return CPI_REQUEST;
+ }
+
+ value = parcJSON_GetValueByName(json, cpiAck);
+ if (value != NULL) {
+ return CPI_ACK;
+ }
+
+ trapIllegalValue(json, "Expected CpiMessageType, actual %s", parcJSON_ToString(json));
+}
+
+/**
+ * You should verify that it's a CPI message with cpi_IsCpiMessage() before using this.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CpiMessageType
+cpi_GetMessageType(const CCNxControl *control)
+{
+ PARCJSON *json = ccnxControl_GetJson(control);
+ CpiMessageType result = controlPlaneInterface_GetCPIMessageType(json);
+ return result;
+}
+
+/**
+ * Returns the inner operation JSON from the request.
+ *
+ * INPUT: "{ CPI_REQUEST: { SEQUENCE:number key: { operation } }}"
+ * OUTPUT: "{ key : { operation } }"
+ *
+ * Example return: "{ operation }"
+ * @param <#param1#>
+ * @return The inner json, do not destroy it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSONPair *
+cpi_ParseRequest(PARCJSON *request)
+{
+ PARCJSONValue *value = parcJSON_GetValueByName(request, cpiRequest);
+ assertNotNull(value, "Could not find JSON key %s in %s", cpiRequest, parcJSON_ToString(request));
+ assertTrue(parcJSONValue_IsJSON(value), "cpiRequest is unexpected type");
+
+ PARCJSON *requestJson = parcJSONValue_GetJSON(value);
+ PARCJSONPair *result = parcJSON_GetPairByIndex(requestJson, 1);
+
+ return result;
+}
+
+CCNxControl *
+cpi_ForwarderVersion()
+{
+ return NULL;
+}
+
+uint64_t
+controlPlaneInterface_GetSequenceNumber(const PARCJSON *controlPlaneMessage)
+{
+ assertNotNull(controlPlaneMessage, "Invalid state, got NULL json from control message");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(controlPlaneMessage, cpiRequest);
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(controlPlaneMessage, cpiResponse);
+ }
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(controlPlaneMessage, cpiAck);
+ }
+
+ assertNotNull(value, "Could not get request or response");
+
+ PARCJSON *json = parcJSONValue_GetJSON(value);
+ value = parcJSON_GetValueByName(json, cpiSeqnum);
+ assertNotNull(value, "Could not retrieve key %s from CPI section", cpiSeqnum);
+
+ return parcJSONValue_GetInteger(value);
+}
+
+/**
+ * All CPI messages carry a sequence number.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+uint64_t
+cpi_GetSequenceNumber(CCNxControl *control)
+{
+ PARCJSON *json = ccnxControl_GetJson(control);
+
+ return controlPlaneInterface_GetSequenceNumber(json);
+}
+
+PARCJSON *
+cpi_CreatePauseInputRequest(void)
+{
+ PARCJSON *operation = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiPause, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+PARCJSON *
+cpi_CreateFlushRequest(void)
+{
+ PARCJSON *operation = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiFlush, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+/**
+ * Given the inner operation member, wrap it in a Request with a sequence number
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *
+cpi_CreateRequest(const char *key, PARCJSON *operation)
+{
+ PARCJSON *result = parcJSON_Create();
+ PARCJSON *request = parcJSON_Create();
+
+ uint64_t seqnum = cpi_GetNextSequenceNumber();
+
+ parcJSON_AddInteger(request, cpiSeqnum, (int) seqnum);
+ parcJSON_AddObject(request, key, operation);
+ parcJSON_AddObject(result, cpiRequest, request);
+ parcJSON_Release(&request);
+
+ return result;
+}
+
+CCNxControl *
+cpi_CreateResponse(CCNxControl *request, PARCJSON *operation)
+{
+ PARCJSON *requestJson = ccnxControl_GetJson(request);
+
+ // use the same key as the request
+ uint64_t seqnum = controlPlaneInterface_GetSequenceNumber(requestJson);
+
+ PARCJSONValue *value = parcJSON_GetValueByName(requestJson, cpiRequest);
+ assertNotNull(value, "Could not get request or response");
+ assertTrue(parcJSONValue_IsJSON(value), "cpiRequest should be a JSON object");
+
+ PARCJSON *operationJson = parcJSONValue_GetJSON(value);
+ PARCJSONPair *pair = parcJSON_GetPairByIndex(operationJson, 1);
+ const PARCBuffer *opKeyBuf = parcJSONPair_GetName(pair);
+ const char *opKey = parcBuffer_ToString(opKeyBuf);
+
+ PARCJSON *response = parcJSON_Create();
+ parcJSON_AddInteger(response, cpiSeqnum, (int) seqnum);
+ parcJSON_AddObject(response, opKey, operation);
+ parcMemory_Deallocate(&opKey);
+
+ PARCJSON *responseJson = parcJSON_Create();
+ parcJSON_AddObject(responseJson, cpiResponse, response);
+ parcJSON_Release(&response);
+
+ CCNxControl *result = ccnxControl_CreateCPIRequest(responseJson);
+
+ parcJSON_Release(&responseJson);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.h b/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.h
new file mode 100644
index 00000000..01b6e861
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/controlPlaneInterface.h
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 controlPlaneInterface.h
+ *
+ * Based loosely on Netlink (RFC 3549)
+ *
+ * Constructs `CCNxControl` for common control plane operations.
+ * These methods do not actually communicate with the transport.
+ * The user must send the message down their protocol stack and await a response.
+ *
+ * CPI messages have a Type: Request, Response, or ACK.
+ * A request may ask for an ACK if successful for commands that would otherwise not
+ * produce a response.
+ * Request that fail always generate a NACK.
+ *
+ * All messages must carry a "sequence number" which must be unique within
+ * the Transport.
+ * Although sequence numbers imply an ordering, they do not imply
+ * causality or precedence.
+ * They only imply a duplicate.
+ *
+ * An ACK is a reponse that carries no data, it just ACKs (or NACKs) a sequence number.
+ * A field in the ACK indicates it is an error (NACK). An ACK carries the original
+ * request and an optional message.
+ *
+ * All messages carry a mandatory sequence number,
+ * which is unique in all messages.
+ * An ACK (or NACK) contains the original message that generated the ACK,
+ * including its sequence number.
+ * These conventions allow one to implement a reliable CPI messaging system, if desired.
+ *
+ * The Control Plane operations are:
+ *
+ * * CPI_INTERFACE_LIST
+ * Return: A response with an array of network interfaces ("interfaceIndex", type, and flags), or a NACK.
+ *
+ * * CPI_REGISTER
+ * Add a FIB entry with the given CCNxName prefix to the specified interfaceIndex.
+ * The value of "-1" means the current interface.
+ * Return: an ACK (or NACK)
+ *
+ * * CPI_UNREGISTER
+ * Remove a FIB entry with the given CCNxName prefix from the specified interfaceIndex.
+ * The value of "-1" means the current interface.
+ * Return: an ACK (or NACK)
+ *
+ * * CPI_FORWARDER_VERSION:
+ * Return: Response (a string) or a NACK.
+ *
+ * * CPI_ADDRESS
+ * Request: by interfaceIndex
+ * Response: the sockaddr_storage list for the interface, or a NACK
+ *
+ * * CPI_PREFIX_REGISTRATION_LIST
+ * Request: by interfaceIndex, value "-1" means the current interface
+ * Response: the list of CCNxNames (with their flags) registered on the interface or a NACK
+ *
+ * * CPI_PAUSE_INPUT
+ * Request: by current connection, causes stack to pause the input (top and bottom)
+ * Response: Forwarder sends ACK up the stack.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ */
+/*
+ * This description needs to be revised based on the recent refactoring.
+ *
+ * The user will be interested in cpi_Forwarding.h and cpi_ManageLinks.h.
+ *
+ * Case 1027: need to specify the memory management, especially for variable sized messages.
+ */
+
+#ifndef libccnx_controlPlaneInterface_h
+#define libccnx_controlPlaneInterface_h
+
+#include <ccnx/transport/common/transport_MetaMessage.h>
+#include <sys/socket.h>
+
+#include <ccnx/api/control/cpi_ControlMessage.h>
+#include <ccnx/api/control/cpi_Forwarding.h>
+#include <ccnx/api/control/cpi_ManageLinks.h>
+#include <ccnx/api/control/cpi_CancelFlow.h>
+#include <ccnx/api/control/cpi_ManageCaches.h>
+#include <ccnx/api/control/cpi_ManageWldr.h>
+
+typedef enum {
+ CPI_REQUEST,
+ CPI_RESPONSE, // a resonse with contents
+ CPI_ACK // a response without contents
+} CpiMessageType;
+
+typedef enum {
+ CPI_ERROR, // a NACK response, carries original message
+ CPI_REGISTER_PREFIX,
+ CPI_UNREGISTER_PREFIX,
+ CPI_FORWARDER_VERSION,
+ CPI_INTERFACE_LIST,
+ CPI_ADDRESS,
+ CPI_PREFIX_REGISTRATION_LIST,
+ CPI_PAUSE,
+ CPI_FLUSH,
+ CPI_CANCEL_FLOW,
+ CPI_CREATE_TUNNEL,
+ CPI_REMOVE_TUNNEL,
+ CPI_CONNECTION_LIST,
+ CPI_ADD_ETHER_CONNECTION,
+ CPI_ADD_CONNECTION_ETHERNET,
+ CPI_REMOVE_CONNECTION_ETHERNET,
+ CPI_ADD_LISTENER,
+ CPI_REMOVE_LISTENER,
+ CPI_CACHE_STORE_ON,
+ CPI_CACHE_STORE_OFF,
+ CPI_CACHE_SERVE_ON,
+ CPI_CACHE_SERVE_OFF,
+ CPI_CACHE_CLEAR,
+ CPI_SET_FORWARDING_STRATEGY,
+ CPI_SET_WLDR
+} CpiOperation;
+
+typedef enum {
+ ACK_ACK,
+ ACK_NACK
+} CpiAckType;
+
+typedef struct control_plane_information {
+ CpiMessageType messageType;
+ CpiOperation operation;
+ uint64_t serialNumber;
+} ControlPlaneInformation;
+
+
+const char *cpiRequest_GetJsonTag();
+const char *cpiResponse_GetJsonTag();
+
+/**
+ * Return the name used in the JSON representation for a control message sequence number.
+ *
+ * @return The name used in the JSON representation for a control message sequence number.
+ *
+ * Example:
+ * @code
+ * {
+ **cpiSequence_GetJSONTag
+ * }
+ * @endcode
+ */
+const char *cpiSequence_GetJSONTag(void);
+
+/**
+ * Get the CpiOperation from the given JSON representation of the CPI command.
+ *
+ * @param [in] json A pointer to a valid PARCJSON instance.
+ *
+ * @return The CpiOperation specified in the JSON.
+ *
+ * Example:
+ * @code
+ * {
+ * const char *sequenceNumberJSONName = cpiSequence_GetJSONTag();
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CpiOperation cpi_getCPIOperation2(const PARCJSON *json);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] control A pointer to a valid CCNxControl instance.
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CpiOperation cpi_GetMessageOperation(CCNxControl *control);
+
+/**
+ * Get the CpiMessageType from the given JSON representation of the CPIMessage
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CpiMessageType controlPlaneInterface_GetCPIMessageType(PARCJSON *json);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CpiMessageType cpi_GetMessageType(const CCNxControl *control);
+
+/**
+ * Get the sequence number of the given Control Plane Message.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+uint64_t controlPlaneInterface_GetSequenceNumber(const PARCJSON *controlPlaneMessage);
+
+/**
+ * All CPI messages carry a sequence number.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+uint64_t cpi_GetSequenceNumber(CCNxControl *control);
+
+/**
+ * Gererate a CPI request
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+#define CPI_CURRENT_INTERFACE 0x7FFFFFFF
+
+/**
+ * Generate a control object to request the forewarder version
+ *
+ * Will cause a CPI Response to be sent back, if the forwarder supports the command.
+ * Otherwise, the ForwarderConnector should send a NACK back.
+ *
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *cpi_ForwarderVersion(void);
+
+/**
+ * Cause the connection to pause input (from the top and bottom).
+ * When the ACk arrives back to the top, caller know there are no
+ * more data messages in the stack.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpi_CreatePauseInputRequest(void);
+
+/**
+ * Creates a message that the forwarder connector will ACK. Once the
+ * ACK with the corresponding sequence number is received, the sender knows
+ * that all prior messages had been handled by the forwarder connector.
+ *
+ * @return non-null An allocted JSON object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpi_CreateFlushRequest(void);
+
+/**
+ * Given the inner operation member, wrap it in a Request with a sequence number
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpi_CreateRequest(const char *key, PARCJSON *operation);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CCNxControl *cpi_CreateResponse(CCNxControl *request, PARCJSON *response);
+
+/**
+ * Create an acknowledgement to the given request (expressed in JSON).
+ *
+ * @param [in] request A pointer to a PARCJSON representation of the request to acknowledge.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to a PARCJSON instance representing the acknowledgement.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiAcks_CreateAck(const PARCJSON *request);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiAcks_CreateNack(const PARCJSON *request);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+bool cpiAcks_IsAck(const PARCJSON *json);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+uint64_t cpiAcks_GetAckOriginalSequenceNumber(const PARCJSON *json);
+#endif // libccnx_controlPlaneInterface_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Acks.c b/libccnx-transport-rta/ccnx/api/control/cpi_Acks.c
new file mode 100644
index 00000000..07bc934a
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Acks.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.
+ */
+
+/**
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+/*
+ * {
+ * "CPI_ACK" : {
+ * "SEQUENCE" : <sequence number>,
+ * "RETURN" : "ACK" or "NACK",
+ * "REQUEST" : <original request JSON>
+ * [, "MESSAGE" : <optional message> ]
+ * }
+ * ["AUTHENTICATOR" : <TBD proof based on request/response, e.g. a crypto signature>]
+ * }
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <strings.h>
+
+#include <LongBow/runtime.h>
+
+#include "controlPlaneInterface.h"
+#include "cpi_private.h"
+#include "cpi_Acks.h"
+
+struct control_plane_ack {
+ ControlPlaneInformation cpi_ack;
+ CpiAckType ack_type;
+ ControlPlaneInformation cpi_original;
+};
+
+static const char *cpiReturn = "RETURN";
+static const char *cpiReturnAck = "ACK";
+static const char *cpiReturnNack = "NACK";
+static const char *cpiOriginal = "REQUEST";
+static const char *cpiRequest = "CPI_REQUEST";
+
+PARCJSON *
+cpiAcks_CreateAck(const PARCJSON *originalRequest)
+{
+ uint64_t seqnum = cpi_GetNextSequenceNumber();
+ PARCJSON *body = parcJSON_Create();
+
+ parcJSON_AddInteger(body, cpiSeqnum, (int) seqnum);
+ parcJSON_AddString(body, cpiReturn, cpiReturnAck);
+
+ PARCJSON *copy = parcJSON_Copy(originalRequest);
+ parcJSON_AddObject(body, cpiOriginal, copy);
+ parcJSON_Release(&copy);
+
+ PARCJSON *json = parcJSON_Create();
+
+ parcJSON_AddObject(json, cpiAck, body);
+ parcJSON_Release(&body);
+
+ return json;
+}
+
+PARCJSON *
+cpiAcks_CreateNack(const PARCJSON *request)
+{
+ uint64_t seqnum = cpi_GetNextSequenceNumber();
+ PARCJSON *body = parcJSON_Create();
+ parcJSON_AddInteger(body, cpiSeqnum, (int) seqnum);
+ parcJSON_AddString(body, cpiReturn, cpiReturnNack);
+
+ PARCJSON *copy = parcJSON_Copy(request);
+ parcJSON_AddObject(body, cpiOriginal, copy);
+ parcJSON_Release(&copy);
+
+ PARCJSON *json = parcJSON_Create();
+ parcJSON_AddObject(json, cpiAck, body);
+ parcJSON_Release(&body);
+
+ return json;
+}
+
+bool
+cpiAcks_IsAck(const PARCJSON *json)
+{
+ PARCJSONValue *ack_value = parcJSON_GetValueByName(json, cpiAck);
+ if (ack_value != NULL) {
+ PARCJSON *ack_json = parcJSONValue_GetJSON(ack_value);
+ PARCJSONValue *return_value = parcJSON_GetValueByName(ack_json, cpiReturn);
+ PARCBuffer *sBuf = parcJSONValue_GetString(return_value);
+ const char *returnStr = parcBuffer_Overlay(sBuf, 0);
+ return strcasecmp(returnStr, cpiReturnAck) == 0;
+ }
+ return false;
+}
+
+uint64_t
+cpiAcks_GetAckOriginalSequenceNumber(const PARCJSON *json)
+{
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiAck);
+ assertNotNull(value, "got null ack json: %s", parcJSON_ToString(json));
+
+ PARCJSON *tempJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(tempJson, cpiOriginal);
+ assertNotNull(value, "got null original json from the ack: %s", parcJSON_ToString(tempJson));
+
+ tempJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(tempJson, cpiRequest);
+ assertNotNull(value, "got null request json from the ack: %s", parcJSON_ToString(tempJson));
+
+ tempJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(tempJson, cpiSeqnum);
+ assertNotNull(value, "got null seqnum inside the request: %s", parcJSON_ToString(tempJson));
+
+ return parcJSONValue_GetInteger(value);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Acks.h b/libccnx-transport-rta/ccnx/api/control/cpi_Acks.h
new file mode 100644
index 00000000..36ad0441
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Acks.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Acks.h
+ *
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+#ifndef libccnx_cpi_Acks_h
+#define libccnx_cpi_Acks_h
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <parc/algol/parc_JSON.h>
+
+struct control_plane_ack;
+typedef struct control_plane_ack CPIAck;
+
+#define cpiAck "CPI_ACK"
+#define cpiSeqnum "SEQUENCE"
+
+/**
+ * Create a CPIAck instance from a PARCJSON instance.
+ *
+ * @param [in] json A pointer to a valid PARCJSON instance.
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A pointer to a valid CPIAck instance.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ */
+CPIAck *cpiAcks_ParseJSON(const PARCJSON *json);
+
+/**
+ * Create a CPIAck instance.
+ *
+ * @param [in] sequenceNumber The sequence number for the ACK.
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A pointer to a valid CPIAck instance.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ */
+CPIAck *cpiAcks_Create(uint64_t sequenceNumber);
+
+/**
+ * Create a CPIAck instance, from a template of the original request.
+ *
+ * @param [in] originalRequest A pointer to a valid PARCJSON instance containint he original request.
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A pointer to a valid PARCJSON instance.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ */
+PARCJSON *cpiAcks_CreateAck(const PARCJSON *originalRequest);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ */
+PARCJSON *cpiAcks_CreateNack(const PARCJSON *request);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+bool cpiAcks_IsAck(const PARCJSON *json);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ */
+uint64_t cpiAcks_GetAckOriginalSequenceNumber(const PARCJSON *json);
+#endif // libccnx_cpi_Acks_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Address.c b/libccnx-transport-rta/ccnx/api/control/cpi_Address.c
new file mode 100644
index 00000000..6dd44f69
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Address.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <strings.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+#include <ccnx/api/control/cpi_Address.h>
+
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Base64.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Hash.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_Network.h>
+
+#include <LongBow/runtime.h>
+
+const char *cpiAddressType = "ADDRESSTYPE";
+const char *cpiAddrData = "DATA";
+
+struct cpi_address {
+ CPIAddressType addressType;
+ PARCBuffer *blob;
+};
+
+static struct cpi_address_type_str {
+ CPIAddressType type;
+ const char *str;
+} cpiAddressTypeString[] = {
+ { .type = cpiAddressType_INET, .str = "INET" },
+ { .type = cpiAddressType_INET6, .str = "INET6" },
+ { .type = cpiAddressType_LINK, .str = "LINK" },
+ { .type = cpiAddressType_IFACE, .str = "IFACE" },
+ { .type = cpiAddressType_UNIX, .str = "UNIX" },
+ { .type = 0, .str = NULL }
+};
+
+void
+cpiAddress_Destroy(CPIAddress **addressPtr)
+{
+ assertNotNull(addressPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*addressPtr, "Parameter must dereference to non-null pointer");
+
+ CPIAddress *address = *addressPtr;
+ parcBuffer_Release(&address->blob);
+ parcMemory_Deallocate((void **) &address);
+ *addressPtr = NULL;
+}
+
+void
+cpiAddress_AssertValid(const CPIAddress *address)
+{
+ assertNotNull(address, "Parameter must be non-null CPIAddress *");
+}
+
+const char *
+cpiAddress_TypeToString(CPIAddressType type)
+{
+ for (int i = 0; cpiAddressTypeString[i].str != NULL; i++) {
+ if (cpiAddressTypeString[i].type == type) {
+ return cpiAddressTypeString[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown value: %d", type);
+}
+
+CPIAddressType
+cpiAddress_StringToType(const char *str)
+{
+ for (int i = 0; cpiAddressTypeString[i].str != NULL; i++) {
+ if (strcasecmp(cpiAddressTypeString[i].str, str) == 0) {
+ return cpiAddressTypeString[i].type;
+ }
+ }
+ trapIllegalValue(str, "Unknown type '%s'", str);
+}
+
+static CPIAddress *
+_cpiAddress_Create(CPIAddressType addressType, PARCBuffer *buffer)
+{
+ CPIAddress *result = parcMemory_AllocateAndClear(sizeof(CPIAddress));
+
+ assertNotNull(result, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIAddress));
+ if (result != NULL) {
+ result->addressType = addressType;
+ result->blob = buffer;
+ }
+ return result;
+}
+
+CPIAddress *
+cpiAddress_CreateFromInet(struct sockaddr_in *addr_in)
+{
+ assertNotNull(addr_in, "Parameter must be non-null");
+
+ addr_in->sin_family = AF_INET;
+
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in));
+ parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in), (uint8_t *) addr_in);
+ parcBuffer_Flip(buffer);
+
+ CPIAddress *result = _cpiAddress_Create(cpiAddressType_INET, buffer);
+
+ return result;
+}
+
+CPIAddress *
+cpiAddress_CreateFromInet6(struct sockaddr_in6 *addr_in6)
+{
+ assertNotNull(addr_in6, "Parameter must be non-null");
+
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6));
+ parcBuffer_PutArray(buffer, sizeof(struct sockaddr_in6), (uint8_t *) addr_in6);
+ parcBuffer_Flip(buffer);
+
+ CPIAddress *result = _cpiAddress_Create(cpiAddressType_INET6, buffer);
+
+ return result;
+}
+
+CPIAddress *
+cpiAddress_CreateFromLink(const uint8_t *linkaddr, size_t length)
+{
+ assertNotNull(linkaddr, "Parameter must be non-null");
+
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_in6));
+ parcBuffer_PutArray(buffer, length, linkaddr);
+ parcBuffer_Flip(buffer);
+
+ CPIAddress *result = _cpiAddress_Create(cpiAddressType_LINK, buffer);
+ return result;
+}
+
+CPIAddress *
+cpiAddress_CreateFromInterface(unsigned interfaceIndex)
+{
+ unsigned netbyteorder = htonl(interfaceIndex);
+
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(netbyteorder));
+ parcBuffer_PutArray(buffer, sizeof(netbyteorder), (uint8_t *) &netbyteorder);
+ parcBuffer_Flip(buffer);
+
+ CPIAddress *result = _cpiAddress_Create(cpiAddressType_IFACE, buffer);
+ return result;
+}
+
+CPIAddress *
+cpiAddress_CreateFromUnix(struct sockaddr_un *addr_un)
+{
+ assertNotNull(addr_un, "Parameter must be non-null");
+
+ PARCBuffer *buffer = parcBuffer_Allocate(sizeof(struct sockaddr_un));
+ parcBuffer_PutArray(buffer, sizeof(struct sockaddr_un), (uint8_t *) addr_un);
+ parcBuffer_Flip(buffer);
+
+ CPIAddress *result = _cpiAddress_Create(cpiAddressType_UNIX, buffer);
+ return result;
+}
+
+CPIAddress *
+cpiAddress_Copy(const CPIAddress *original)
+{
+ cpiAddress_AssertValid(original);
+
+ CPIAddress *result = _cpiAddress_Create(original->addressType, parcBuffer_Copy(original->blob));
+ return result;
+}
+
+bool
+cpiAddress_Equals(const CPIAddress *a, const CPIAddress *b)
+{
+ if (a == b) {
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (a->addressType == b->addressType) {
+ if (parcBuffer_Equals(a->blob, b->blob)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+PARCJSON *
+cpiAddress_ToJson(const CPIAddress *address)
+{
+ cpiAddress_AssertValid(address);
+
+ PARCJSON *json = parcJSON_Create();
+ PARCBufferComposer *encoded = parcBase64_Encode(parcBufferComposer_Create(), address->blob);
+
+ // we need a NULL at the end of the string.
+ parcBufferComposer_PutUint8(encoded, 0);
+ PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(encoded);
+ char *str = parcBuffer_Overlay(buffer, 0);
+
+ parcJSON_AddString(json, cpiAddressType, cpiAddress_TypeToString(address->addressType));
+ parcJSON_AddString(json, cpiAddrData, str);
+
+ parcBuffer_Release(&buffer);
+ parcBufferComposer_Release(&encoded);
+
+ return json;
+}
+
+CPIAddress *
+cpiAddress_CreateFromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *addrFamilyValue = parcJSON_GetValueByName(json, cpiAddressType);
+
+ assertNotNull(addrFamilyValue, "json is not valid, missing %s: %s", cpiAddressType, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsString(addrFamilyValue),
+ "%s key is not a number: %s", cpiAddressType, parcJSON_ToString(json));
+
+ PARCJSONValue *addrDataValue = parcJSON_GetValueByName(json, cpiAddrData);
+
+ assertNotNull(addrDataValue, "json is not valid, missing %s: %s", cpiAddrData, parcJSON_ToString(json));
+
+ PARCBufferComposer *composer =
+ parcBase64_Decode(parcBufferComposer_Create(), parcJSONValue_GetString(addrDataValue));
+ PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer);
+ parcBufferComposer_Release(&composer);
+
+ PARCBuffer *sBuf = parcJSONValue_GetString(addrFamilyValue);
+
+ CPIAddress *result =
+ _cpiAddress_Create(cpiAddress_StringToType(parcBuffer_Overlay(sBuf, 0)), buffer);
+
+ return result;
+}
+
+CPIAddressType
+cpiAddress_GetType(const CPIAddress *address)
+{
+ cpiAddress_AssertValid(address);
+
+ return address->addressType;
+}
+
+// The Get functions need better names, what they do (Get from what? Put to what?)
+// is not clear from their names. Case 1028
+bool
+cpiAddress_GetInet(const CPIAddress *address, struct sockaddr_in *addr_in)
+{
+ cpiAddress_AssertValid(address);
+ assertNotNull(addr_in, "Parameter addr_in must be non-null");
+
+ if (address->addressType == cpiAddressType_INET) {
+ assertTrue(parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in),
+ "CPIAddress corrupted. Expected length %zu, actual length %zu",
+ sizeof(struct sockaddr_in),
+ parcBuffer_Remaining(address->blob));
+
+ memcpy(addr_in, parcBuffer_Overlay(address->blob, 0), sizeof(struct sockaddr_in));
+ return true;
+ }
+ return false;
+}
+
+bool
+cpiAddress_GetInet6(const CPIAddress *address, struct sockaddr_in6 *addr_in6)
+{
+ cpiAddress_AssertValid(address);
+ assertNotNull(addr_in6, "Parameter addr_in6 must be non-null");
+
+ if (address->addressType == cpiAddressType_INET6) {
+ assertTrue(parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_in6),
+ "CPIAddress corrupted. Expected length %zu, actual length %zu",
+ sizeof(struct sockaddr_in6),
+ parcBuffer_Remaining(address->blob));
+
+ memcpy(addr_in6, parcBuffer_Overlay(address->blob, 0), sizeof(struct sockaddr_in6));
+ return true;
+ }
+ return false;
+}
+
+bool
+cpiAddress_GetUnix(const CPIAddress *address, struct sockaddr_un *addr_un)
+{
+ cpiAddress_AssertValid(address);
+ assertNotNull(addr_un, "Parameter addr_in6 must be non-null");
+
+ if (address->addressType == cpiAddressType_UNIX) {
+ assertTrue(parcBuffer_Remaining(address->blob) == sizeof(struct sockaddr_un),
+ "CPIAddress corrupted. Expected length %zu, actual length %zu",
+ sizeof(struct sockaddr_un),
+ parcBuffer_Remaining(address->blob));
+
+ memcpy(addr_un, parcBuffer_Overlay(address->blob, 0), sizeof(struct sockaddr_un));
+ return true;
+ }
+ return false;
+}
+
+bool
+cpiAddress_GetInterfaceIndex(const CPIAddress *address, uint32_t *ifidx)
+{
+ cpiAddress_AssertValid(address);
+ assertNotNull(ifidx, "Parameter ifidx must be non-null");
+
+ if (address->addressType == cpiAddressType_IFACE) {
+ assertTrue(parcBuffer_Remaining(address->blob) == sizeof(uint32_t),
+ "CPIAddress corrupted. Expected length %zu, actual length %zu",
+ sizeof(uint32_t),
+ parcBuffer_Remaining(address->blob));
+
+ uint32_t netbyteorder;
+ memcpy(&netbyteorder, parcBuffer_Overlay(address->blob, 0), sizeof(uint32_t));
+ *ifidx = ntohl(netbyteorder);
+ return true;
+ }
+ return false;
+}
+
+PARCBuffer *
+cpiAddress_GetLinkAddress(const CPIAddress *address)
+{
+ cpiAddress_AssertValid(address);
+ if (address->addressType == cpiAddressType_LINK) {
+ return address->blob;
+ }
+ return NULL;
+}
+
+static PARCBufferComposer *
+_Inet_BuildString(const CPIAddress *address, PARCBufferComposer *composer)
+{
+ cpiAddress_AssertValid(address);
+
+ struct sockaddr_in *saddr = (struct sockaddr_in *) parcBuffer_Overlay(address->blob, 0);
+ return parcNetwork_SockInet4Address_BuildString(saddr, composer);
+}
+
+static PARCBufferComposer *
+_Inet6_BuildString(const CPIAddress *address, PARCBufferComposer *composer)
+{
+ cpiAddress_AssertValid(address);
+
+ struct sockaddr_in6 *saddr = (struct sockaddr_in6 *) parcBuffer_Overlay(address->blob, 0);
+ return parcNetwork_SockInet6Address_BuildString(saddr, composer);
+}
+
+static PARCBufferComposer *
+_Link_BuildString(const CPIAddress *address, PARCBufferComposer *composer)
+{
+ cpiAddress_AssertValid(address);
+
+ const unsigned char *addr = parcBuffer_Overlay(address->blob, 0);
+
+ size_t length = parcBuffer_Remaining(address->blob);
+
+ return parcNetwork_LinkAddress_BuildString(addr, length, composer);
+}
+
+static ssize_t
+_UnixToString(char *output, size_t remaining_size, const PARCBuffer *addr)
+{
+ assertNotNull(output, "parameter output must be non-null");
+ parcBuffer_AssertValid(addr);
+
+ assertTrue(parcBuffer_Remaining(addr) == sizeof(struct sockaddr_un),
+ "CPIAddress corrupted. Expected %zu actual %zu",
+ sizeof(struct sockaddr_un), parcBuffer_Remaining(addr));
+
+ // sockaddr length for the path, 16 for the ascii stuff, 3 for the length number
+ struct sockaddr_un *saddr = (struct sockaddr_un *) parcBuffer_Overlay((PARCBuffer *) addr, 0);
+ size_t min_remaining = strlen(saddr->sun_path) + 16 + 3;
+ assertTrue(remaining_size >= min_remaining,
+ "Remaining size too small, need at least %zu", min_remaining);
+
+ ssize_t output_length = sprintf(output, "{ .path=%s, .len=%zu }", saddr->sun_path, strlen(saddr->sun_path));
+ return output_length;
+}
+
+static ssize_t
+_IfaceToString(char *output, size_t remaining_size, const PARCBuffer *addr)
+{
+ assertNotNull(output, "parameter output must be non-null");
+ parcBuffer_AssertValid(addr);
+
+ assertTrue(parcBuffer_Remaining(addr) == sizeof(uint32_t),
+ "CPIAddress corrupted. Expected %zu actual %zu",
+ sizeof(uint32_t), parcBuffer_Remaining(addr));
+
+ uint32_t *ifidx = (uint32_t *) parcBuffer_Overlay((PARCBuffer *) addr, 0);
+
+ ssize_t output_length = sprintf(output, "{ .ifidx=%u }", ntohl(*ifidx));
+
+ return output_length;
+}
+
+PARCBufferComposer *
+cpiAddress_BuildString(const CPIAddress *address, PARCBufferComposer *composer)
+{
+ if (address != NULL) {
+ char *str = cpiAddress_ToString(address);
+ parcBufferComposer_PutString(composer, str);
+ parcMemory_Deallocate((void **) &str);
+ }
+ return composer;
+}
+
+char *
+cpiAddress_ToString(const CPIAddress *address)
+{
+ cpiAddress_AssertValid(address);
+
+ char addrstr[256];
+
+ switch (address->addressType) {
+ case cpiAddressType_INET: {
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(_Inet_BuildString(address, composer));
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+ return result;
+ }
+ break;
+
+ case cpiAddressType_INET6: {
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(_Inet6_BuildString(address, composer));
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+
+ parcBufferComposer_Release(&composer);
+ return result;
+ }
+ break;
+
+ case cpiAddressType_UNIX:
+ _UnixToString(addrstr, 256, address->blob);
+ break;
+
+ case cpiAddressType_LINK: {
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(_Link_BuildString(address, composer));
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+
+ parcBufferComposer_Release(&composer);
+ return result;
+ }
+ break;
+
+ case cpiAddressType_IFACE:
+ _IfaceToString(addrstr, 256, address->blob);
+ break;
+
+ default:
+ sprintf(addrstr, "UNKNOWN type = %d", address->addressType);
+ break;
+ }
+
+ ssize_t alloc_size = 1024;
+ char *output = parcMemory_Allocate(alloc_size);
+ assertNotNull(output, "parcMemory_Allocate(%zu) returned NULL", alloc_size);
+ ssize_t output_length = snprintf(output, alloc_size, "{ .type=%s, .data=%s }", cpiAddress_TypeToString(address->addressType), addrstr);
+
+ assertTrue(output_length < alloc_size, "allocated size too small, needed %zd", output_length);
+ assertFalse(output_length < 0, "snprintf error: (%d) %s", errno, strerror(errno));
+
+ return output;
+}
+
+PARCHashCode
+cpiAddress_HashCode(const CPIAddress *address)
+{
+ cpiAddress_AssertValid(address);
+
+ PARCHashCode hash = parcBuffer_HashCode(address->blob);
+ hash = parcHashCode_HashImpl((uint8_t *) &address->addressType, sizeof(address->addressType), hash);
+
+ return hash;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Address.h b/libccnx-transport-rta/ccnx/api/control/cpi_Address.h
new file mode 100644
index 00000000..94a7b0ca
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Address.h
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Address.h
+ * @brief Represents an endpoint address.
+ *
+ * Represents an endpoint address. May be INET, INET6, or a multi-byte LINK,
+ * or an Interface Index.
+ *
+ * INET and INET6 must contain the .sa_addr member, and other members as needed
+ * by the use of the address.
+ *
+ * The Interface Index address is essentially a pointer to a device.
+ *
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+#ifndef libccnx_cpi_Address_h
+#define libccnx_cpi_Address_h
+
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <stdbool.h>
+
+#include <parc/algol/parc_JSON.h>
+
+#include <parc/algol/parc_Buffer.h>
+#include <parc/algol/parc_BufferComposer.h>
+
+typedef enum {
+ cpiAddressType_INET = 1,
+ cpiAddressType_INET6 = 2,
+ cpiAddressType_LINK = 3,
+ cpiAddressType_IFACE = 4,
+ cpiAddressType_UNIX = 5 /* PF_UNIX */
+} CPIAddressType;
+
+/**
+ * Return a string representation of the given `CPIAddressType`
+ *
+ * @param [in] type A valid CPIAddressType value.
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a static string representation of the `CPIAddressType`.
+ *
+ * Example:
+ * @code
+ * {
+ * const char *typeAsString = cpiAddress_TypeToString(cpiAddressType_INET);
+ * }
+ * @endcode
+ *
+ * @see cpiAddress_StringToType
+ */
+const char *cpiAddress_TypeToString(CPIAddressType type);
+
+/**
+ * Return a `CPIAddressType` from the given nul-terminated C string.
+ *
+ * This induces a LongBow trap for an illegal value.
+ *
+ * @param [in] typeAsString A nul-terminated, C string representation of a `CPIAddressType`.
+ *
+ * @return A CPIAddressType
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddressType type = cpiAddress_TypeToString("INET");
+ * }
+ * @endcode
+ *
+ * @see cpiAddress_TypeToString
+ */
+CPIAddressType cpiAddress_StringToType(const char *typeAsString);
+
+struct cpi_address;
+typedef struct cpi_address CPIAddress;
+
+/**
+ * Create a new `CPIAddress` instance from an IPv4 IP address, the port is optional.
+ *
+ * The sockaddr_in should be filled in network byte order. The newly created instance must
+ * eventually be destroyed by calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] addr_in The `sockaddr_in` representing the IPv4 IP address with which to initialize the new `CPIAddress` instance.
+ * @return A new instance of `CPIAddress` that must eventually be destroyed by calling {@link cpiAddress_Destroy}().
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *dest = cpiAddress_CreateFromInet(
+ * &(struct sockaddr_in) {
+ * .sa_addr = inet_addr("foo.bar.com"),
+ * .sa_port = htons(9695) } );
+ * cpiAddress_Destroy(&dest);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_CreateFromInet(struct sockaddr_in *addr_in);
+
+/**
+ * Create a new `CPIAddress` instance from an IPv6 IP address, the port is optional.
+ *
+ *
+ * The sockaddr_in should be filled in network byte order. The newly created instance must
+ * eventually be destroyed by calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] addr_in6 A `sockaddr_in6` from which to initialize a new instance of CPIAddress
+ * @return A new instance of `CPIAddress` that must eventually be destroyed by calling {@link cpiAddress_Destroy}()
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in6 addr_in6;
+ * memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+ *
+ * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ * addr_in6.sin6_family = AF_INET6;
+ * addr_in6.sin6_port = 0x0A0B;
+ * addr_in6.sin6_flowinfo = 0x01020304;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_CreateFromInet6(struct sockaddr_in6 *addr_in6);
+
+/**
+ * Create a new `CPIAddress` instance, initialized from a Link address.
+ *
+ * User must know the link address format (i.e. token ring vs ethernet) and have the address in a byte array.
+ * The array is encoded in left-to-right order. The newly created instance must eventually be destroyed by
+ * calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] linkaddr A byte array containing the link address
+ * @param [in] length The length of the link address byte array
+ * @return A new instance of `CPIAddress` that must eventually be destroyed by calling {@link cpiAddress_Destroy}()
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 };
+ * CPIAddress *address = cpiAddress_CreateFromLink(mac, sizeof(mac));
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_CreateFromLink(const uint8_t *linkaddr, size_t length);
+
+/**
+ * Create a new `CPIAddress` instance from a network interface index.
+ *
+ * The interfaceIndex should be in host byte order. The newly created instance must eventually be destroyed by
+ * calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] interfaceIndex The index of the interface to encode
+ * @return A new instance of `CPIAddress` that must eventually be destroyed by calling {@link cpiAddress_Destroy}()
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(2);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_CreateFromInterface(uint32_t interfaceIndex);
+
+/**
+ * Create a new CPIAddress instance from a PF_UNIX address domain.
+ *
+ * The newly created instance must eventually be destroyed by calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] addr_un The `struct sockaddr_un` specifying the local PF_UNIX socket address
+ * @return A new instance of `CPIAddress` that must eventually be destroyed by calling {@link cpiAddress_Destroy}()
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_un addr_unix;
+ * memset(&addr_unix, 0, sizeof(struct sockaddr_un));
+ * char path[] = "/Hello/Cruel/World";
+ * strcpy(addr_un.sun_path, path);
+ * addr_un.sun_family = AF_UNIX;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromUnix(&addr_un);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_CreateFromUnix(struct sockaddr_un *addr_un);
+
+/**
+ * Create a deep copy of an instance of a `CPIAddress`. A completely new, indedependent instance is created.
+ *
+ * The newly created instance must eventually be destroyed by calling {@link cpiAddress_Destroy}().
+ *
+ * @param [in] original A pointer to a `CPIAddress` instance to be copied.
+ * @return A new instance of a CPIAddress, deep copied from the `original` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(2);
+ *
+ * CPIAddress *copy = cpiAddress_Copy(address);
+ *
+ * cpiAddress_Destroy(&address);
+ * cpiAddress_Destroy(&copy);
+ * }
+ * @endcode
+ * @see cpiAddress_Destroy
+ */
+CPIAddress *cpiAddress_Copy(const CPIAddress *original);
+
+/**
+ * Deallocate an instance of a CPIAddress.
+ *
+ * The CPIAddress instance is deallocated, and any referenced data is also deallocated.
+ * The referenced pointer is set to NULL upon return.
+ *
+ * @param [in] addressPtr A pointer to a pointer to an instance of CPIAddress.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(2);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ */
+void cpiAddress_Destroy(CPIAddress **addressPtr);
+
+/**
+ * Create a new PARCJSON instance representing the specified `CPIAddress` instance.
+ *
+ * The newly created PARCJSON instance must eventually be destroyed by calling {@link parcJSON_Release}().
+ *
+ * @param [in] address A pointer to a CPIAddress instance.
+ * @return A new PARCJSON instance representing the specified `address`.
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in6 addr_in6;
+ * memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+ *
+ * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ * addr_in6.sin6_family = AF_INET6;
+ * addr_in6.sin6_port = 0x0A0B;
+ * addr_in6.sin6_flowinfo = 0x01020304;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+ *
+ * PARCJSON *json = cpiAddress_ToJson(address);
+ *
+ * CPIAddress *address2 = cpiAddress_CreateFromJson(json);
+ *
+ * cpiAddress_Destroy(&address);
+ * cpiAddress_Destroy(&address2);
+ * parcJSON_Release(&json);
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiAddress_ToJson(const CPIAddress *address);
+
+/**
+ * Create a new PARCJSON instance from a JSON description of an address.
+ *
+ * The JSON passed in should look like `{ "LABEL" : { "ADDRESSTYPE" : integer, "DATA" : base_64_data } }`.
+ * The newly created PARCJSON instance must eventually be destroyed by calling {@link parcJSON_Release}().
+ *
+ * The value of "LABEL" does not matter, but the inner structure must be as specified.
+ *
+ * The ADDRESSTYPE is one of {@link CPIAddressType}.
+ *
+ * @param [in] json A pointer to a PARCJSON instance describing an address
+ * @return A newly created CPIAddress instance
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in6 addr_in6;
+ * memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+ *
+ * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ * addr_in6.sin6_family = AF_INET6;
+ * addr_in6.sin6_port = 0x0A0B;
+ * addr_in6.sin6_flowinfo = 0x01020304;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+ *
+ * PARCJSON *json = cpiAddress_ToJson(address);
+ *
+ * CPIAddress *address2 = cpiAddress_CreateFromJson(json);
+ *
+ * cpiAddress_Destroy(&address);
+ * cpiAddress_Destroy(&address2);
+ * parcJSON_Release(&json);
+ * }
+ * @endcode
+ */
+CPIAddress *cpiAddress_CreateFromJson(PARCJSON *json);
+
+/**
+ * Determine if two CPIAddress instances are equal.
+ *
+ *
+ * The following equivalence relations on non-null `CPIAddress` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `cpiAddress_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiAddress_Equals(x, y)` must return true if and only if
+ * `cpiAddress_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiAddress_Equals(x, y)` returns true and
+ * `cpiAddress_Equals(y, z)` returns true,
+ * then `cpiAddress_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiAddress_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiAddress_Equals(x, NULL)` must
+ * return false.
+ *
+ * If one address specifies more information than other,
+ * e.g. a is INET with a port and b is not, they are not equal.
+ *
+ * `a` and `b` may be NULL, and NULL == NULL.
+ *
+ * @param a A pointer to a CPIAddress instance
+ * @param b A pointer to a CPIAddress instance
+ * @return true if the two instances are equal
+ * @return false if the two instances are not equal
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(2);
+ * CPIAddress *copy = cpiAddress_Copy(address);
+ *
+ * if (cpiAddress_Equals(address, copy)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ *
+ * cpiAddress_Destroy(&address);
+ * cpiAddress_Destroy(&copy);
+ * }
+ * @endcode
+ */
+bool cpiAddress_Equals(const CPIAddress *a, const CPIAddress *b);
+
+/**
+ * Return the {@link CPIAddressType} from a specified CPIAddress.
+ *
+ * @param [in] A pointer to a CPIAddress instance
+ *
+ * @return the {@link CPIAddressType} of the specified CPIAddress instance
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(2);
+ *
+ * CPIAddressType type = cpiAddress_GetType(address);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ *
+ * @see CPIAddressType
+ */
+CPIAddressType cpiAddress_GetType(const CPIAddress *address);
+
+/**
+ * Fills in the output parameter with an INET address.
+ *
+ * @param addr_in must be non-NULL
+ * @return true if INET address and output filled in, false otherwise.
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in6 addr_in6;
+ * memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+ *
+ * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ * addr_in6.sin6_family = AF_INET6;
+ * addr_in6.sin6_port = 0x0A0B;
+ * addr_in6.sin6_flowinfo = 0x01020304;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+ *
+ * struct sockaddr_in6 addr_test;
+ * bool success = cpiAddress_GetInet6(address, &addr_test);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ */
+bool cpiAddress_GetInet(const CPIAddress *address, struct sockaddr_in *addr_in);
+
+/**
+ * Retrieve the INET6 address associated with a `CPIAddress` instance.
+ *
+ * If the specified CPIAddress instance is of type {@link cpiAddressType_INET6}, then
+ * populate the supplied `struct sockaddr_in6` from the CPIAddress and return true. If the
+ * CPIAddress is not of type `cpiAddressType_INET6`, this function returns false.
+ *
+ * @param [in] address A pointer to a `CPIAddress` instance of type {@link cpiAddressType_INET6}.
+ * @param [in] addr_in6 A pointer to a `struct sockaddr_in6`. Must be non-NULL.
+ * @return true If the CPIAddress instance is of type `cpiAddressType_INET6` and `addr_in6` was filled in
+ * @return false If the CPIAddress instance was not of type `cpiAddressType_INET6` or `addr_in6` could not be filled in.
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in6 addr_in6;
+ * memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+ *
+ * inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ * addr_in6.sin6_family = AF_INET6;
+ * addr_in6.sin6_port = 0x0A0B;
+ * addr_in6.sin6_flowinfo = 0x01020304;
+ *
+ * CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+ *
+ * struct sockaddr_in6 addr_test;
+ * bool success = cpiAddress_GetInet6(address, &addr_test);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_GetType
+ */
+bool cpiAddress_GetInet6(const CPIAddress *address, struct sockaddr_in6 *addr_in6);
+
+/**
+ * Retrieve the interface index associated with a `CPIAddress` instance.
+ *
+ * If the specified `CPIAddress` instance is of type {@link cpiAddressType_IFACE}, then
+ * populate the supplied `uint32_t` from the CPIAddress and return true. If the
+ * `CPIAddress` is not of type `cpiAddressType_INET6`, this function returns false.
+ *
+ * @param [in] address A pointer to a `CPIAddress` instance of type {@link cpiAddressType_IFACE}.
+ * @param [in] interfaceIndex A pointer to a `uint32_t` to fill in. Must be non-NULL.
+ * @return true If the CPIAddress instance is of type `cpiAddressType_IFACE` and `interfaceIndex` was filled in.
+ * @return false If the CPIAddress instance was not of type `cpiAddressType_IFACE` or `interfaceIndex` could not be filled in.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(6);
+ *
+ * uint32_t test;
+ * bool success = cpiAddress_GetInterfaceIndex(address, &test);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_GetType
+ */
+bool cpiAddress_GetInterfaceIndex(const CPIAddress *address, uint32_t *interfaceIndex);
+
+/**
+ * Retrieve the link address associated with a `CPIAddress` instance.
+ *
+ * If the specified `CPIAddress` instance is of type {@link cpiAddressType_LINK}, then return a pointer
+ * to the {@link PARCBuffer} containing the link address. If the `CPIAddress` is not of type {@link cpiAddressType_LINK},
+ * then return NULL. The returned PARCBuffer pointer points to memory managed by the CPIAddress instance, and
+ * does not need to be destroyed or released on its own.
+ *
+ * @param [in] address A pointer to a `CPIAddress` instance of type {@link cpiAddressType_LINK}.
+ * @return A pointer to the {@link PARCBuffer} containing the link address.
+ *
+ * Example:
+ * @code
+ * {
+ * uint8_t mac[] = { 0x14, 0x10, 0x9f, 0xd7, 0x0b, 0x89 };
+ * CPIAddress *address = cpiAddress_CreateFromLink(mac, sizeof(mac));
+ *
+ * PARCBuffer *macBuffer = cpiAddress_GetLinkAddress(address);
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see cpiAddress_GetType
+ */
+PARCBuffer *cpiAddress_GetLinkAddress(const CPIAddress *address);
+
+/**
+ * Append the string representation of a `CPIAddress` to a specified `PARCBufferComposer`.
+ *
+ * @param [in] address A pointer to a `CPIAddress` instance.
+ * @param [in] composer A pointer to a `PARCBufferComposer` instance to which to append the string.
+ *
+ * @return The `PARCBufferComposer` instance that was passed in.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(1);
+ * PARCBufferComposer *composer = cpiAddress_BuildString(address, parcBufferComposer_Create());
+ * parcBufferComposer_Release(&composer);
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ *
+ * @see PARCBufferComposer
+ */
+PARCBufferComposer *cpiAddress_BuildString(const CPIAddress *address, PARCBufferComposer *composer);
+
+/**
+ * Produce a nil-terminated string representation of the specified instance.
+ *
+ * The result must be freed by the caller via {@link parcMemory_Deallocate}.
+ *
+ * @param [in] interest A pointer to the instance.
+ *
+ * @return NULL Cannot allocate memory.
+ * @return non-NULL A pointer to an allocated, nul-terminated C string that must be deallocated via {@link parcMemory_Deallocate}().
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddress *address = cpiAddress_CreateFromInterface(1);
+ *
+ * char *string = cpiAddress_ToString(address);
+ *
+ * if (string != NULL) {
+ * printf("CPIAddress looks like: %s\n", string);
+ * parcMemory_Deallocate(string);
+ * } else {
+ * printf("Cannot allocate memory\n");
+ * }
+ *
+ * cpiAddress_Destroy(&address);
+ * }
+ * @endcode
+ * @see parcMemory_Deallocate
+ * @see cpiAddress_BuildString
+ */
+char *cpiAddress_ToString(const CPIAddress *address);
+
+/**
+ * Return a non-cryptographic hash code consistent with Equals
+ *
+ * If cpiAddressA == cpiAddressB, then cpiAddress_HashCode(cpiAddressA) == cpiAddress_HashCode(cpiAddressB)
+ *
+ * @param [in] address A pointer to a CPIAddress instance.
+ * @return A 32-bit hashcode for the specified CPIAddress instance.
+ *
+ * Example:
+ * @code
+ * CPIAddress *address = cpiAddress_CreateFromInterface(1);
+ *
+ * uint32_t hashCode = cpiAddress_HashCode(address);
+ *
+ * cpiAddress_Destroy(&address);
+ * @endcode
+ */
+PARCHashCode cpiAddress_HashCode(const CPIAddress *address);
+#endif // libccnx_cpi_Address_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.c b/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.c
new file mode 100644
index 00000000..a33800ad
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/cpi_AddressList.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Buffer.h>
+
+struct cpi_addresslist {
+ PARCArrayList *listOfCPIAddress;
+};
+
+static void
+_cpiAddressList_FreeAddress(void **addressVoidPtr)
+{
+ CPIAddress **addressPtr = (CPIAddress **) addressVoidPtr;
+ cpiAddress_Destroy(addressPtr);
+}
+
+CPIAddressList *
+cpiAddressList_Create()
+{
+ CPIAddressList *list = parcMemory_AllocateAndClear(sizeof(CPIAddressList));
+ assertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIAddressList));
+ list->listOfCPIAddress = parcArrayList_Create(_cpiAddressList_FreeAddress);
+ assertNotNull(list->listOfCPIAddress, "Got null from parcArrayList_Create");
+
+ return list;
+}
+
+void
+cpiAddressList_Destroy(CPIAddressList **addressListPtr)
+{
+ assertNotNull(addressListPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*addressListPtr, "Parameter must dereference to non-null pointer");
+ CPIAddressList *list = *addressListPtr;
+
+ parcArrayList_Destroy(&list->listOfCPIAddress);
+ parcMemory_Deallocate((void **) &list);
+ *addressListPtr = NULL;
+}
+
+CPIAddressList *
+cpiAddressList_Append(CPIAddressList *list, CPIAddress *address)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ assertNotNull(address, "Parameter address must be non-null");
+
+ parcArrayList_Add(list->listOfCPIAddress, (PARCObject *) address);
+ return list;
+}
+
+CPIAddressList *
+cpiAddressList_Copy(const CPIAddressList *original)
+{
+ assertNotNull(original, "Parameter must be non-null");
+
+ CPIAddressList *copy = cpiAddressList_Create();
+ for (int i = 0; i < parcArrayList_Size(original->listOfCPIAddress); i++) {
+ CPIAddress *address = (CPIAddress *) parcArrayList_Get(original->listOfCPIAddress, i);
+ parcArrayList_Add(copy->listOfCPIAddress, (PARCObject *) cpiAddress_Copy(address));
+ }
+
+ return copy;
+}
+
+bool
+cpiAddressList_Equals(const CPIAddressList *a, const CPIAddressList *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a == b) {
+ return true;
+ }
+
+ if (parcArrayList_Size(a->listOfCPIAddress) != parcArrayList_Size(b->listOfCPIAddress)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < parcArrayList_Size(a->listOfCPIAddress); i++) {
+ const CPIAddress *addr_a = (CPIAddress *) parcArrayList_Get(a->listOfCPIAddress, i);
+ const CPIAddress *addr_b = (CPIAddress *) parcArrayList_Get(b->listOfCPIAddress, i);
+ if (!cpiAddress_Equals(addr_a, addr_b)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+size_t
+cpiAddressList_Length(const CPIAddressList *list)
+{
+ assertNotNull(list, "Parameter must be non-null");
+ return parcArrayList_Size(list->listOfCPIAddress);
+}
+
+const CPIAddress *
+cpiAddressList_GetItem(const CPIAddressList *list, size_t item)
+{
+ assertNotNull(list, "Parameter must be non-null");
+ assertTrue(item < cpiAddressList_Length(list), "Asked for item %zu beyond end of list %zu", item, cpiAddressList_Length(list));
+
+ return (CPIAddress *) parcArrayList_Get(list->listOfCPIAddress, item);
+}
+
+/**
+ * Returns a JSON array of the addresses
+ *
+ * { [ {addr0}, {addr1}, ..., {addrN} ] }
+ *
+ * @param <#param1#>
+ * @return A JSON array, even if array empty
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSONArray *
+cpiAddressList_ToJson(const CPIAddressList *list)
+{
+ assertNotNull(list, "Parameter must be non-null");
+ PARCJSONArray *array = parcJSONArray_Create();
+
+ for (size_t i = 0; i < cpiAddressList_Length(list); i++) {
+ const CPIAddress *addr = cpiAddressList_GetItem(list, i);
+ PARCJSON *json = cpiAddress_ToJson(addr);
+ PARCJSONValue *value = parcJSONValue_CreateFromJSON(json);
+ parcJSON_Release(&json);
+ parcJSONArray_AddValue(array, value);
+ parcJSONValue_Release(&value);
+ }
+
+ return array;
+}
+
+/**
+ * Creates an address list based on a JSON array
+ *
+ * { [ {addr0}, {addr1}, ..., {addrN} ] }
+ *
+ * @param <#param1#>
+ * @return An allocated address list.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+
+CPIAddressList *
+cpiAddressList_CreateFromJson(PARCJSONArray *array)
+{
+ assertNotNull(array, "Parameter must be non-null");
+ CPIAddressList *list = cpiAddressList_Create();
+
+ for (size_t i = 0; i < parcJSONArray_GetLength(array); i++) {
+ PARCJSONValue *value = parcJSONArray_GetValue(array, i);
+ PARCJSON *addrjson = parcJSONValue_GetJSON(value);
+ CPIAddress *addr = cpiAddress_CreateFromJson(addrjson);
+ cpiAddressList_Append(list, addr);
+ }
+
+ return list;
+}
+
+char *
+cpiAddressList_ToString(const CPIAddressList *list)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ for (size_t i = 0; i < cpiAddressList_Length(list); i++) {
+ char *addressString = cpiAddress_ToString(cpiAddressList_GetItem(list, i));
+ parcBufferComposer_PutString(composer, addressString);
+ if (i < (cpiAddressList_Length(list) - 1)) {
+ parcBufferComposer_PutString(composer, " ");
+ }
+ parcMemory_Deallocate((void **) &addressString);
+ }
+
+ PARCBuffer *buffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(buffer);
+ parcBuffer_Release(&buffer);
+ parcBufferComposer_Release(&composer);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.h b/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.h
new file mode 100644
index 00000000..235531f9
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_AddressList.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 cpi_AddressList.h
+ * @brief A list of CPIAddress instances.
+ *
+ * An AddressList is a list of addresses.
+ * It wraps a PARCLinkedList for type saftey with CPIAddress.
+ *
+ */
+#ifndef libccnx_cpi_AddressList_h
+#define libccnx_cpi_AddressList_h
+
+#include <ccnx/api/control/cpi_Address.h>
+
+struct cpi_addresslist;
+/**
+ * @typedef CPIAddressList
+ * @abstract A list of CPIAddress instance pointers.
+ */
+typedef struct cpi_addresslist CPIAddressList;
+
+/**
+ * Create an instance of {@link CPIAddressList}
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid CPIAddressList instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddressList *list = cpiAddressList_Create();
+ *
+ * }
+ * @endcode
+ *
+ * @see cpiAddressList_Destroy
+ */
+CPIAddressList *cpiAddressList_Create(void);
+
+/**
+ * Dellocate and destroy a CPIAddressList instance.
+ *
+ * @param [in] addressListPtr A pointer to a pointer to a valid {@link CPIAddressList}.
+ *
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddressList *list = cpiAddressList_Create(void);
+ * cpiAddressList_Destroy(&list);
+ * }
+ * @endcode
+ *
+ * @see cpiAddressList_Create
+ */
+void cpiAddressList_Destroy(CPIAddressList **addressListPtr);
+
+/**
+ * Appends the address, taking ownership of the memory
+ *
+ * @param list A pointer to a CPIAddressList.
+ * @param address must be non-null
+ * @return The input list
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIAddressList *cpiAddressList_Append(CPIAddressList *list, CPIAddress *address);
+
+/**
+ * Creates a reference counted copy
+ *
+ * @param list A pointer to a valid {@link CPIAddressList}.
+ *
+ * @return An allocated list, you must destroy it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIAddressList *cpiAddressList_Copy(const CPIAddressList *list);
+
+/**
+ * Determine if two CPIAddressList instances are equal.
+ *
+ * Two CPIAddressList instances are equal if, and only if, they have the same length,
+ * with the same elements in the same order.
+ *
+ *
+ * The following equivalence relations on non-null `CPIAddressList` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIAddressList_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `CPIAddressList_Equals(x, y)` must return true if and only if
+ * `cpiAddressList_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiAddressList_Equals(x, y)` returns true and
+ * `cpiAddressList_Equals(y, z)` returns true,
+ * then `cpiAddressList_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiAddressList_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiAddressList_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIAddressList` instance.
+ * @param b A pointer to a `CPIAddressList` instance.
+ * @return true if the two `CPIAddressList` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIAddressList *a = cpiAddressList_Create();
+ * CPIAddressList *b = cpiAddressList_Create();
+ *
+ * if (cpiAddressList_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiAddressList_Equals(const CPIAddressList *a, const CPIAddressList *b);
+
+/**
+ * Get the number of items in the list
+ *
+ * @param list A pointer to a {@link CPIAddressList}.
+ * @return The number of items in the list.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t cpiAddressList_Length(const CPIAddressList *list);
+
+/**
+ * Returns a const reference to an item.
+ * Use CPIAddress_Copy if needed.
+ *
+ * Do not free or modify the returned value.
+ * Use CPIAddress_Copy if you need a mutable instance.
+ *
+ * @param list A pointer to a CPIAddressList.
+ * @param item A value less than the number of items in the given {@link CPIAddressList}.
+ * @return Asserts if item off end of list.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const CPIAddress *cpiAddressList_GetItem(const CPIAddressList *list, size_t item);
+
+/**
+ * Returns a JSON array of the addresses
+ *
+ * { "ADDRS" : [ {addr0}, {addr1}, ..., {addrN} ] }
+ *
+ * @param list A pointer to a CPIAddressList.
+ * @return A JSON object, even if array empty
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSONArray *cpiAddressList_ToJson(const CPIAddressList *list);
+
+/**
+ * Creates an address list based on a JSON array
+ *
+ * { "ADDRS" : [ {addr0}, {addr1}, ..., {addrN} ] }
+ *
+ * @param json A pointer to a valid PARCJSON instance.
+ * @return An allocated address list.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIAddressList *cpiAddressList_CreateFromJson(PARCJSONArray *json);
+
+/**
+ * Get a nul-terminated, C-string representation of the given {@link CPIAddressList}.
+ *
+ * @param list A pointer to a valid {@link CPIAddressList} instance.
+ *
+ * @return An allocate string representation of the {@link CPIAddressList} that must be freed via `parcMemory_Deallocate()`.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+char *cpiAddressList_ToString(const CPIAddressList *list);
+#endif // libccnx_cpi_AddressList_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.c b/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.c
new file mode 100644
index 00000000..4898282b
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/controlPlaneInterface.h>
+#include <ccnx/api/control/cpi_CancelFlow.h>
+#include <parc/algol/parc_Memory.h>
+
+static const char *cpiCancelFlow = "CPI_CANCEL_FLOW";
+static const char *cpiFlowName = "FLOW_NAME";
+
+PARCJSON *
+cpiCancelFlow_CreateRequest(const CCNxName *name)
+{
+ PARCJSON *operation = parcJSON_Create();
+
+ char *uri = ccnxName_ToString(name);
+ parcJSON_AddString(operation, cpiFlowName, uri);
+ parcMemory_Deallocate((void **) &uri);
+
+ PARCJSON *result = cpi_CreateRequest(cpiCancelFlow, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+PARCJSON *
+cpiCancelFlow_Create(const CCNxName *name)
+{
+ PARCJSON *operation = parcJSON_Create();
+
+ char *uri = ccnxName_ToString(name);
+ parcJSON_AddString(operation, cpiFlowName, uri);
+ parcMemory_Deallocate((void **) &uri);
+
+ PARCJSON *result = cpi_CreateRequest(cpiCancelFlow, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+CCNxName *
+cpiCancelFlow_GetFlowName(const PARCJSON *controlMessage)
+{
+ assertNotNull(controlMessage, "Parameter controlMessage must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(controlMessage, cpiRequest_GetJsonTag());
+ assertNotNull(value, "only support getting the name from a Request at the moment, not from an ack/nack.");
+ PARCJSON *inner_json = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(inner_json, cpiCancelFlow_CancelFlowJsonTag());
+ assertNotNull(value, "Missing JSON tag in control message: %s", cpiCancelFlow_CancelFlowJsonTag());
+ inner_json = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(inner_json, cpiFlowName);
+ assertNotNull(value, "Missing JSON tag in control message: %s", cpiFlowName);
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ const char *uri = parcBuffer_Overlay(sBuf, 0);
+
+ CCNxName *name = ccnxName_CreateFromCString(uri);
+
+ return name;
+}
+
+CCNxName *
+cpiCancelFlow_NameFromControlMessage(CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ return cpiCancelFlow_GetFlowName(ccnxControl_GetJson(control));
+}
+
+bool
+cpiCancelFlow_SuccessFromResponse(CCNxControl *control)
+{
+ trapNotImplemented("cpiCancelFlow_SuccessFromResponse");
+}
+
+const char *
+cpiCancelFlow_CancelFlowJsonTag(void)
+{
+ return cpiCancelFlow;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.h b/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.h
new file mode 100644
index 00000000..77f846e5
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_CancelFlow.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_CancelFlow.h
+ * @brief Cancel a "flow"
+ *
+ */
+#ifndef libccnx_cpi_CancelFlow_h
+#define libccnx_cpi_CancelFlow_h
+
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+#include <ccnx/common/ccnx_Name.h>
+
+/**
+ * Creates a CPI reqeust to cancel a flow
+ *
+ * Will return an asynchronous ACK or NACK.
+ *
+ * @param name The CCNxName of the flow to cancel.
+ * @return A pointer to a valid CPIControlMessage
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiCancelFlow_CreateRequest(const CCNxName *name);
+
+/**
+ * Creates a CPI reqeust to cancel a flow
+ *
+ * @param [in] name The CCNxName of the flow to cancel.
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid PARCJSON instance.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+PARCJSON *cpiCancelFlow_Create(const CCNxName *name);
+
+/**
+ * Return the CCNxName associated with the given control message
+ *
+ * @param controlMessage A pointer to a control message.
+ * @return A pointer to a valid CCNxName instance.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxName *cpiCancelFlow_GetFlowName(const PARCJSON *controlMessage);
+
+/**
+ * Return the name associated with the message
+ *
+ * @param controlMessage A pointer to a control message.
+ * @return A pointer to a valid CCNxName instance.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxName *cpiCancelFlow_NameFromControlMessage(CCNxControl *controlMessage);
+
+/**
+ * Given a CPI response (ACK or NACK) return the success state
+ *
+ * @param controlMessage A pointer to a control message.
+ * @return true if the control message signals success.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiCancelFlow_SuccessFromResponse(CCNxControl *controlMessage);
+
+/**
+ * The CPI tag used for cancel flow
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiCancelFlow_CancelFlowJsonTag(void);
+#endif // libccnx_cpi_CancelFlow_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Connection.c b/libccnx-transport-rta/ccnx/api/control/cpi_Connection.c
new file mode 100644
index 00000000..19ae8c1d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Connection.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_Object.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_JSON.h>
+
+#include <ccnx/api/control/cpi_Connection.h>
+#include <ccnx/api/control/cpi_InterfaceGeneric.h>
+
+#define SOURCE_INDEX 0
+#define DESTINATION_INDEX 1
+
+const static char cpiIFIDX[] = "IFIDX";
+const static char cpiSRCADDR[] = "SRC";
+const static char cpiDSTADDR[] = "DST";
+const static char cpiCONNTYPE[] = "CONNTYPE";
+const static char cpiSTATE[] = "STATE";
+
+
+struct cpi_connection {
+ CPIInterfaceGeneric *generic;
+ CPIConnectionType tunnelType;
+};
+
+struct connection_type_string_s {
+ CPIConnectionType type;
+ const char *str;
+} connectionTypeStrings[] = {
+ { .type = cpiConnection_UDP, .str = "UDP" },
+ { .type = cpiConnection_TCP, .str = "TCP" },
+ { .type = cpiConnection_GRE, .str = "GRE" },
+ { .type = cpiConnection_MULTICAST, .str = "MCAST" },
+ { .type = cpiConnection_L2, .str = "L2" },
+ { .type = 0, .str = NULL },
+};
+
+
+static void
+_cpiConnection_Destroy(CPIConnection **iptunPtr)
+{
+ assertNotNull(iptunPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*iptunPtr, "Parameter must dereference to non-null pointer");
+
+ CPIConnection *iptun = *iptunPtr;
+ cpiInterfaceGeneric_Destroy(&iptun->generic);
+}
+
+parcObject_ExtendPARCObject(CPIConnection, _cpiConnection_Destroy, cpiConnection_Copy, cpiConnection_ToString, cpiConnection_Equals, NULL, NULL, cpiConnection_ToJson);
+
+parcObject_ImplementAcquire(cpiConnection, CPIConnection);
+
+parcObject_ImplementRelease(cpiConnection, CPIConnection);
+
+static PARCBufferComposer *
+cpiConnection_BuildString(const CPIConnection *connection, PARCBufferComposer *composer)
+{
+ cpiInterfaceGeneric_BuildString(connection->generic, composer);
+ cpiConnectionType_BuildString(connection->tunnelType, composer);
+
+ return composer;
+}
+
+char *
+cpiConnection_ToString(const CPIConnection *connection)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ cpiConnection_BuildString(connection, composer);
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+ return result;
+}
+
+PARCBufferComposer *
+cpiConnectionType_BuildString(CPIConnectionType type, PARCBufferComposer *composer)
+{
+ return parcBufferComposer_PutStrings(composer, " ", cpiConnectionType_ToString(type), NULL);
+}
+
+const char *
+cpiConnectionType_ToString(CPIConnectionType type)
+{
+ for (int i = 0; connectionTypeStrings[i].str != NULL; i++) {
+ if (connectionTypeStrings[i].type == type) {
+ return connectionTypeStrings[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %d", type);
+}
+
+CPIConnectionType
+cpiConnectionType_FromString(const char *str)
+{
+ for (int i = 0; connectionTypeStrings[i].str != NULL; i++) {
+ if (strcasecmp(connectionTypeStrings[i].str, str) == 0) {
+ return connectionTypeStrings[i].type;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %s", str);
+}
+
+CPIConnection *
+cpiConnection_Create(unsigned ifidx, CPIAddress *source, CPIAddress *destination, CPIConnectionType tunnelType)
+{
+ assertNotNull(source, "Parameter source must be non-null");
+ assertNotNull(destination, "Parameter destination must be non-null");
+
+ CPIConnection *iptun = parcObject_CreateInstance(CPIConnection);
+ assertNotNull(iptun, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIConnection));
+
+ CPIAddressList *addrlist = cpiAddressList_Create();
+ cpiAddressList_Append(addrlist, source);
+ cpiAddressList_Append(addrlist, destination);
+
+ iptun->generic = cpiInterfaceGeneric_Create(ifidx, addrlist);
+ iptun->tunnelType = tunnelType;
+ return iptun;
+}
+
+CPIConnection *
+cpiConnection_Copy(const CPIConnection *original)
+{
+ assertNotNull(original, "Parameter original must be non-null");
+ CPIConnection *iptun = parcObject_CreateInstance(CPIConnection);
+ assertNotNull(iptun, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIConnection));
+ iptun->generic = cpiInterfaceGeneric_Copy(original->generic);
+ iptun->tunnelType = original->tunnelType;
+ return iptun;
+}
+
+void
+cpiConnection_SetState(CPIConnection *iptun, CPIInterfaceStateType state)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ cpiInterfaceGeneric_SetState(iptun->generic, state);
+}
+
+unsigned
+cpiConnection_GetIndex(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return cpiInterfaceGeneric_GetIndex(iptun->generic);
+}
+
+const CPIAddress *
+cpiConnection_GetSourceAddress(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ const CPIAddressList *addrs = cpiInterfaceGeneric_GetAddresses(iptun->generic);
+ return cpiAddressList_GetItem(addrs, SOURCE_INDEX);
+}
+
+const CPIAddress *
+cpiConnection_GetDestinationAddress(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ const CPIAddressList *addrs = cpiInterfaceGeneric_GetAddresses(iptun->generic);
+ return cpiAddressList_GetItem(addrs, DESTINATION_INDEX);
+}
+
+CPIConnectionType
+cpiConnection_GetConnectionType(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return iptun->tunnelType;
+}
+
+CPIInterfaceStateType
+cpiConnection_GetState(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return cpiInterfaceGeneric_GetState(iptun->generic);
+}
+
+bool
+cpiConnection_Equals(const CPIConnection *a, const CPIConnection *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a->tunnelType == b->tunnelType) {
+ return cpiInterfaceGeneric_Equals(a->generic, b->generic);
+ }
+ return false;
+}
+
+static const char *cpiConnection = "Connection";
+
+PARCJSON *
+cpiConnection_ToJson(const CPIConnection *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+
+ PARCJSON *inner_json = parcJSON_Create();
+
+ parcJSON_AddInteger(inner_json, cpiIFIDX, cpiConnection_GetIndex(iptun));
+
+ if (cpiConnection_GetState(iptun) != CPI_IFACE_UNKNOWN) {
+ parcJSON_AddString(inner_json, cpiSTATE, cpiInterfaceStateType_ToString(cpiConnection_GetState(iptun)));
+ }
+
+ parcJSON_AddString(inner_json, cpiCONNTYPE, cpiConnectionType_ToString(cpiConnection_GetConnectionType(iptun)));
+
+ PARCJSON *json = cpiAddress_ToJson(cpiConnection_GetSourceAddress(iptun));
+ parcJSON_AddObject(inner_json, cpiSRCADDR, json);
+ parcJSON_Release(&json);
+
+ json = cpiAddress_ToJson(cpiConnection_GetDestinationAddress(iptun));
+ parcJSON_AddObject(inner_json, cpiDSTADDR, json);
+ parcJSON_Release(&json);
+
+ PARCJSON *outter_json = parcJSON_Create();
+ parcJSON_AddObject(outter_json, cpiConnection, inner_json);
+ parcJSON_Release(&inner_json);
+
+ return outter_json;
+}
+
+CPIConnection *
+cpiConnection_CreateFromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiConnection);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiConnection,
+ parcJSON_ToString(json));
+
+ PARCJSON *connectionJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(connectionJson, cpiIFIDX);
+ assertNotNull(value,
+ "Could not find key %s: %s", cpiIFIDX, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsNumber(value),
+ "%s is not a number: %s",
+ cpiIFIDX,
+ parcJSON_ToString(json));
+ PARCJSONValue *ifidx_value = value;
+
+ value = parcJSON_GetValueByName(connectionJson, cpiCONNTYPE);
+ assertNotNull(value,
+ "Could not find key %s: %s", cpiCONNTYPE, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsString(value),
+ "%s is not a number: %s",
+ cpiCONNTYPE,
+ parcJSON_ToString(json));
+ PARCJSONValue *tuntype_value = value;
+
+ value = parcJSON_GetValueByName(connectionJson, cpiSRCADDR);
+ assertNotNull(value,
+ "Could not find key %s: %s", cpiSRCADDR, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "%s is not an array: %s",
+ cpiSRCADDR,
+ parcJSON_ToString(json));
+ PARCJSONValue *srcaddr_value = value;
+
+ value = parcJSON_GetValueByName(connectionJson, cpiDSTADDR);
+ assertNotNull(value,
+ "Could not find key %s: %s", cpiDSTADDR, parcJSON_ToString(json));
+ PARCJSONValue *dstaddr_value = value;
+ assertTrue(parcJSONValue_IsJSON(value),
+ "%s is not an array: %s",
+ cpiDSTADDR,
+ parcJSON_ToString(json));
+
+ unsigned ifidx = (unsigned) parcJSONValue_GetInteger(ifidx_value);
+ CPIAddress *srcaddr =
+ cpiAddress_CreateFromJson(parcJSONValue_GetJSON(srcaddr_value));
+ CPIAddress *dstaddr =
+ cpiAddress_CreateFromJson(parcJSONValue_GetJSON(dstaddr_value));
+ PARCBuffer *sbuf = parcJSONValue_GetString(tuntype_value);
+ CPIConnectionType tunnelType =
+ cpiConnectionType_FromString(parcBuffer_Overlay(sbuf, 0));
+
+ CPIConnection *iptun = cpiConnection_Create(ifidx, srcaddr, dstaddr, tunnelType);
+
+ PARCJSONValue *state_value = parcJSON_GetValueByName(connectionJson, cpiSTATE);
+ if (state_value != NULL) {
+ sbuf = parcJSONValue_GetString(state_value);
+ cpiConnection_SetState(iptun, cpiInterfaceStateType_FromString(parcBuffer_Overlay(sbuf, 0)));
+ }
+
+ return iptun;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Connection.h b/libccnx-transport-rta/ccnx/api/control/cpi_Connection.h
new file mode 100644
index 00000000..dea866e8
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Connection.h
@@ -0,0 +1,320 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Connection.h
+ * @brief Represents a point-to-point tunnel over IP.
+ *
+ * The carries can be UDP, TCP, or GRE
+ *
+ * We use InterfaceGeneric to back this type. We always use 2 addresses in the address list.
+ * Address 0 is the source and address 1 is the destination.
+ *
+ */
+#ifndef libccnx_cpi_Connection_h
+#define libccnx_cpi_Connection_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+
+struct cpi_connection;
+typedef struct cpi_connection CPIConnection;
+
+typedef enum {
+ cpiConnection_GRE,
+ cpiConnection_TCP,
+ cpiConnection_UDP,
+ cpiConnection_MULTICAST,
+ cpiConnection_L2
+} CPIConnectionType;
+
+/**
+ * Return a static, nul-terminated C string representing the given `CPIConnectionType`
+ *
+ * @param type A valid CPIConnectionType value.
+ * @return A static, nul-terminated C string
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiConnectionType_ToString(CPIConnectionType type);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionType cpiConnectionType_FromString(const char *typeAsString);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCBufferComposer *cpiConnectionType_BuildString(CPIConnectionType type, PARCBufferComposer *composer);
+
+/**
+ * A representation of a Connection, being two addresses and a type
+ *
+ * <#Discussion#>
+ *
+ * @param ifidx The interface index
+ * @param source is the local address
+ * @param destination is the remote address
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnection *cpiConnection_Create(unsigned ifidx, CPIAddress *source, CPIAddress *destination, CPIConnectionType connType);
+
+
+CPIConnection *cpiConnection_Acquire(const CPIConnection *conn);
+
+/**
+ * Creates a reference counted copy
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated copy, you must destroy it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnection *cpiConnection_Copy(const CPIConnection *conn);
+
+/**
+ * Reference counted release
+ *
+ * Only on the last reference will the call free the contents.
+ *
+ * @param <#param1#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiConnection_Release(CPIConnection **connPtr);
+
+/**
+ * A connection may be up, down, or don't know state.
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiConnection_SetState(CPIConnection *conn, CPIInterfaceStateType state);
+
+/**
+ * Returns the interface index
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return The interface index
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+unsigned cpiConnection_GetIndex(const CPIConnection *conn);
+
+/**
+ * The source address
+ *
+ * This is not a copy, it is the pointer to what is in the object. If you
+ * want to save it, make a copy.
+ *
+ * @param <#param1#>
+ * @return Do not destroy it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const CPIAddress *cpiConnection_GetSourceAddress(const CPIConnection *conn);
+
+/**
+ * The destination (remote) address
+ *
+ * This is not a copy, it is the pointer to what is in the object. If you
+ * want to save it, make a copy.
+ *
+ * @param <#param1#>
+ * @return Do not destroy it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const CPIAddress *cpiConnection_GetDestinationAddress(const CPIConnection *conn);
+
+/**
+ * The type of connection
+ *
+ * A connection may be a TCP tunnel, UDP tunnel, IP multicast overlay,
+ * PF_LOCAL connection, or a layer 2 connection.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionType cpiConnection_GetConnectionType(const CPIConnection *conn);
+
+/**
+ * The connection state, Up, Down, or Don't Know
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceStateType cpiConnection_GetState(const CPIConnection *conn);
+
+/**
+ * Determine if two CPIConnection instances are equal.
+ *
+ * Two CPIConnection instances are equal if, and only if,
+ * (a) the interface index is the same, (b) the connection types are the same,
+ * (c) the connection state is the same, (d) the source address are the same, and
+ * (e) the destination addresses are the same.
+ *
+ * The following equivalence relations on non-null `CPIConnection` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIConnection_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiConnection_Equals(x, y)` must return true if and only if
+ * `cpiConnection_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiConnection_Equals(x, y)` returns true and
+ * `cpiConnection_Equals(y, z)` returns true,
+ * then `cpiConnection_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiConnection_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiConnection_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIConnection` instance.
+ * @param b A pointer to a `CPIConnection` instance.
+ * @return true if the two `CPIConnection` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIConnection *a = cpiConnection_Create();
+ * CPIConnection *b = cpiConnection_Create();
+ *
+ * if (cpiConnection_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiConnection_Equals(const CPIConnection *a, const CPIConnection *b);
+
+/**
+ * A JSON representation of the connection
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated object that you must destroy
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiConnection_ToJson(const CPIConnection *conn);
+
+/**
+ * Creates a Connection object based on a JSON representation.
+ *
+ * Will assert if there's a parsing error
+ *
+ * @param <#param1#>
+ * @return An allocated connection that you must destroy
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnection *cpiConnection_CreateFromJson(PARCJSON *json);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+char *cpiConnection_ToString(const CPIConnection *connection);
+#endif // libccnx_cpi_Connection_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.c b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.c
new file mode 100644
index 00000000..be4793fe
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/cpi_ConnectionEthernet.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_JSON.h>
+
+#include <ccnx/api/control/controlPlaneInterface.h>
+extern uint64_t cpi_GetNextSequenceNumber(void);
+
+// JSON keys
+static const char *KEY_IFNAME = "IFNAME";
+static const char *KEY_ADDR = "PEER_ADDR";
+static const char *KEY_ETHERTYPE = "ETHERTYPE";
+static const char *KEY_SYMBOLIC = "SYMBOLIC";
+
+static const char *KEY_ADDETHER = "AddConnEther";
+static const char *KEY_REMOVEETHER = "RemoveConnEther";
+
+struct cpi_connection_ethernet {
+ char *interfaceName;
+ char *symbolic;
+ CPIAddress *peerLinkAddress;
+ uint16_t ethertype;
+};
+
+CPIConnectionEthernet *
+cpiConnectionEthernet_Create(const char *interfaceName, CPIAddress *peerLinkAddress, uint16_t ethertype, const char *symbolic)
+{
+ assertNotNull(interfaceName, "Parameter interfaceName must be non-null");
+ assertNotNull(peerLinkAddress, "Parameter peerLinkAddress must be non-null");
+
+ CPIConnectionEthernet *etherConn = parcMemory_AllocateAndClear(sizeof(CPIConnectionEthernet));
+ if (etherConn) {
+ etherConn->interfaceName = parcMemory_StringDuplicate(interfaceName, strlen(interfaceName));
+ etherConn->symbolic = parcMemory_StringDuplicate(symbolic, strlen(symbolic));
+ etherConn->peerLinkAddress = cpiAddress_Copy(peerLinkAddress);
+ etherConn->ethertype = ethertype;
+ }
+
+ return etherConn;
+}
+
+void
+cpiConnectionEthernet_Release(CPIConnectionEthernet **etherConnPtr)
+{
+ assertNotNull(etherConnPtr, "Parameter etherConnPtr must be non-null double pointer");
+ assertNotNull(*etherConnPtr, "Parameter etherConnPtr dereference to non-null pointer");
+
+ CPIConnectionEthernet *etherConn = *etherConnPtr;
+ cpiAddress_Destroy(&etherConn->peerLinkAddress);
+ parcMemory_Deallocate((void **) &(etherConn->interfaceName));
+ parcMemory_Deallocate((void **) &(etherConn->symbolic));
+ parcMemory_Deallocate((void **) &etherConn);
+ *etherConnPtr = NULL;
+}
+
+bool
+cpiConnectionEthernet_Equals(const CPIConnectionEthernet *a, const CPIConnectionEthernet *b)
+{
+ if ((a == NULL && b == NULL) || a == b) {
+ // both null or identically equal
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ // only one is null
+ return false;
+ }
+
+ if (a->ethertype == b->ethertype) {
+ if (cpiAddress_Equals(a->peerLinkAddress, b->peerLinkAddress)) {
+ if (strcmp(a->interfaceName, b->interfaceName) == 0) {
+ if (strcmp(a->symbolic, b->symbolic) == 0) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+static PARCJSON *
+_cpiConnectionEthernet_ToJson(const CPIConnectionEthernet *etherConn)
+{
+ PARCJSON *json = parcJSON_Create();
+
+ // ------ Interface Name
+ parcJSON_AddString(json, KEY_IFNAME, etherConn->interfaceName);
+
+ // ------ Symbolic Name
+ parcJSON_AddString(json, KEY_SYMBOLIC, etherConn->symbolic);
+
+ // ------ Link Address
+ PARCJSON *peerLinkJson = cpiAddress_ToJson(etherConn->peerLinkAddress);
+ parcJSON_AddObject(json, KEY_ADDR, peerLinkJson);
+ parcJSON_Release(&peerLinkJson);
+
+ // ------ EtherType
+ parcJSON_AddInteger(json, KEY_ETHERTYPE, etherConn->ethertype);
+
+ return json;
+}
+
+/*
+ * We want to create a JSON object that looks like this
+ * {
+ * "CPI_REQUEST" :
+ * { "SEQUENCE" : <sequence number>,
+ * <operationName> : { "IFNAME" : "em1", "SYMBOLIC" : "conn0", "PEER_ADDR" : { "ADDRESSTYPE" : "LINK", "DATA" : "AQIDBAUG" }, "ETHERTYPE" : 2049 },
+ * }
+ * }
+ */
+static CCNxControl *
+_cpiConnectionEthernet_CreateControlMessage(const CPIConnectionEthernet *etherConn, const char *operationName)
+{
+ PARCJSON *cpiRequest = parcJSON_Create();
+
+ // --- add the seqnum
+
+ uint64_t seqnum = cpi_GetNextSequenceNumber();
+ parcJSON_AddInteger(cpiRequest, "SEQUENCE", (int) seqnum);
+
+ // -- Add the operation
+ PARCJSON *operation = _cpiConnectionEthernet_ToJson(etherConn);
+ parcJSON_AddObject(cpiRequest, operationName, operation);
+ parcJSON_Release(&operation);
+
+ // -- Do the final encapusulation
+ PARCJSON *final = parcJSON_Create();
+ parcJSON_AddObject(final, cpiRequest_GetJsonTag(), cpiRequest);
+ parcJSON_Release(&cpiRequest);
+
+ // -- Create the CPIControlMessage
+ char *finalString = parcJSON_ToCompactString(final);
+
+ parcJSON_Release(&final);
+
+ PARCJSON *oldJson = parcJSON_ParseString(finalString);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(oldJson);
+ parcJSON_Release(&oldJson);
+
+ parcMemory_Deallocate((void **) &finalString);
+
+ return result;
+}
+
+CCNxControl *
+cpiConnectionEthernet_CreateAddMessage(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ CCNxControl *control = _cpiConnectionEthernet_CreateControlMessage(etherConn, KEY_ADDETHER);
+ return control;
+}
+
+CCNxControl *
+cpiConnectionEthernet_CreateRemoveMessage(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ CCNxControl *control = _cpiConnectionEthernet_CreateControlMessage(etherConn, KEY_REMOVEETHER);
+ return control;
+}
+
+static bool
+_cpiConnectionEthernet_IsMessageType(const CCNxControl *control, const char *operationName)
+{
+ bool isOperation = false;
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *oldJson = ccnxControl_GetJson(control);
+ PARCJSONValue *value = parcJSON_GetValueByName(oldJson, cpiRequest_GetJsonTag());
+
+ if (value != NULL) {
+ PARCJSON *innerJson = parcJSONValue_GetJSON(value);
+ // the second array element is the key we're looking for
+ PARCJSONPair *pair = parcJSON_GetPairByIndex(innerJson, 1);
+ if (pair != NULL) {
+ const char *opKey = parcBuffer_Overlay(parcJSONPair_GetName(pair), 0);
+ if (opKey && strcasecmp(opKey, operationName) == 0) {
+ isOperation = true;
+ }
+ }
+ }
+ }
+
+ return isOperation;
+}
+
+bool
+cpiConnectionEthernet_IsAddMessage(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ return _cpiConnectionEthernet_IsMessageType(control, KEY_ADDETHER);
+}
+
+bool
+cpiConnectionEthernet_IsRemoveMessage(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ return _cpiConnectionEthernet_IsMessageType(control, KEY_REMOVEETHER);
+}
+
+CPIConnectionEthernet *
+cpiConnectionEthernet_FromControl(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+
+ CPIConnectionEthernet *etherConn = NULL;
+
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *oldJson = ccnxControl_GetJson(control);
+ PARCJSONValue *value = parcJSON_GetValueByName(oldJson, cpiRequest_GetJsonTag());
+
+ if (value != NULL) {
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Wrong JSON type for %s, expected JSON: %s",
+ cpiRequest_GetJsonTag(), parcJSON_ToString(oldJson));
+ PARCJSON *requestJson = parcJSONValue_GetJSON(value);
+ // the second array element is the key we're looking for
+ PARCJSONPair *pair = parcJSON_GetPairByIndex(requestJson, 1);
+ const char *opKey = parcBuffer_Overlay(parcJSONPair_GetName(pair), 0);
+ if (opKey && ((strcasecmp(opKey, KEY_ADDETHER) == 0) || strcasecmp(opKey, KEY_REMOVEETHER))) {
+ PARCJSON *opJson = parcJSONValue_GetJSON(parcJSONPair_GetValue(pair));
+
+ // Ok, it is one of our messages, now assemble the pieces
+ value = parcJSON_GetValueByName(opJson, KEY_IFNAME);
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ const char *ifname = parcBuffer_Overlay(sBuf, 0);
+ value = parcJSON_GetValueByName(opJson, KEY_SYMBOLIC);
+ sBuf = parcJSONValue_GetString(value);
+ const char *symbolic = parcBuffer_Overlay(sBuf, 0);
+ value = parcJSON_GetValueByName(opJson, KEY_ETHERTYPE);
+ int ethertype = (int) parcJSONValue_GetInteger(value);
+ value = parcJSON_GetValueByName(opJson, KEY_ADDR);
+ PARCJSON *addrJson = parcJSONValue_GetJSON(value);
+ assertNotNull(addrJson, "JSON missing the key %s", KEY_ADDR);
+
+ CPIAddress *peerAddress = cpiAddress_CreateFromJson(addrJson);
+ assertNotNull(peerAddress, "Failed to decode the peer address from %s", parcJSON_ToString(addrJson));
+
+ etherConn = cpiConnectionEthernet_Create(ifname, peerAddress, (uint16_t) ethertype, symbolic);
+
+ cpiAddress_Destroy(&peerAddress);
+ }
+ }
+ }
+
+ return etherConn;
+}
+
+const char *
+cpiConnectionEthernet_GetInterfaceName(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ return etherConn->interfaceName;
+}
+
+const char *
+cpiConnectionEthernet_GetSymbolicName(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ return etherConn->symbolic;
+}
+
+CPIAddress *
+cpiConnectionEthernet_GetPeerLinkAddress(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ return etherConn->peerLinkAddress;
+}
+
+uint16_t
+cpiConnectionEthernet_GetEthertype(const CPIConnectionEthernet *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ return etherConn->ethertype;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.h b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.h
new file mode 100644
index 00000000..877810b2
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionEthernet.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_ConnectionEthernet.h
+ * @brief Represents an ethernet connection
+ *
+ * An ethernet connection is a (local interface name, remote mac address, ethertype) tuple. A unicast
+ * connection, for example, could be ("em3", 3c:15:c2:e7:c5:ca, 0x0801). The broadcast connection would
+ * be ("em3", ff:ff:ff:ff:ff:ff, 0x0801). You could also use group mac addresses.
+ *
+ * Creating an ethernet connetion in the forwarder sets up an entry in the connection table that
+ * you an then attach routes to. For example, you could add a route to /foo via the connection
+ * ("em3", 3c:15:c2:e7:c5:ca, 0x0801), in which case an Interest would be unicast that way. A route
+ * to a broadcast or group address would broadcast the interest.
+ *
+ */
+
+#ifndef CCNx_Control_API_cpi_ConnectionEthernet_h
+#define CCNx_Control_API_cpi_ConnectionEthernet_h
+
+struct cpi_connection_ethernet;
+typedef struct cpi_connection_ethernet CPIConnectionEthernet;
+
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+/**
+ * Creates a CPIConnectionEthernet object
+ *
+ * The symbolic name represents this connection and may be used by other commands. It must be
+ * unique, otherwise the command will fail when sent to the forwarder.
+ *
+ * @param [in] interfaceName The name of the local interface
+ * @param [in] peerLinkAddress The link layer address of the peer (stores a reference to it)
+ * @param [in] ethertype The ethertype to use (host byte order)
+ * @param [in] symbolic The user-defined symbolic name
+ *
+ * @return non-null An Allocated object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionEthernet *cpiConnectionEthernet_Create(const char *interfaceName, CPIAddress *peerLinkAddress, uint16_t ethertype, const char *symbolic);
+
+/**
+ * Releases a reference count to the object
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in,out] etherConnPtr A pointer to an etherConn object, will be null'd.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiConnectionEthernet_Release(CPIConnectionEthernet **etherConnPtr);
+
+/**
+ * Determine if two CPIConnectionEthernet instances are equal.
+ *
+ * Two CPIConnectionEthernet instances are equal if, and only if,
+ * they are either both null or both non-null and compare
+ * as equal field-for-field over (interfaceName, peerLinkAddress, ethertype, symbolic).
+ *
+ * The interface name is case sensitive, so "ETH0" is not the same as "eth0".
+ *
+ *
+ * The following equivalence relations on non-null `CPIConnectionEthernet` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIConnectionEthernet_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiConnectionEthernet_Equals(x, y)` must return true if and only if
+ * `cpiConnectionEthernet_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiConnectionEthernet_Equals(x, y)` returns true and
+ * `cpiConnectionEthernet_Equals(y, z)` returns true,
+ * then `cpiConnectionEthernet_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiConnectionEthernet_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiConnectionEthernet_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIConnectionEthernet` instance.
+ * @param b A pointer to a `CPIConnectionEthernet` instance.
+ * @return true if the two `CPIConnectionEthernet` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIConnectionEthernet *a = cpiConnectionEthernet_Create();
+ * CPIConnectionEthernet *b = cpiConnectionEthernet_Create();
+ *
+ * if (cpiConnectionEthernet_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+
+bool cpiConnectionEthernet_Equals(const CPIConnectionEthernet *a, const CPIConnectionEthernet *b);
+
+/**
+ * Creates a control message to add the connection
+ *
+ * An add message indicates to the forwarder that it should add the corresponding
+ * Ethernet connection.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return non-null a CPI control message
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *cpiConnectionEthernet_CreateAddMessage(const CPIConnectionEthernet *etherConn);
+
+/**
+ * Creates a control message to remove the connection
+ *
+ * A remove message indicates to the forwarder that it should remove the corresponding
+ * Ethernet connection.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return non-null a CPI control message
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *cpiConnectionEthernet_CreateRemoveMessage(const CPIConnectionEthernet *etherConn);
+
+/**
+ * Checks if the control message is an Add command
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return true Message is an Add command for a ConnectionEthernet
+ * @return false Message is not an Add command for a ConnectionEthernet
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiConnectionEthernet_IsAddMessage(const CCNxControl *control);
+
+/**
+ * Checks if the message is a Remove command
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] control A CCNx Control message
+ *
+ * @return true Message is an Remove command for a ConnectionEthernet
+ * @return false Message is not Remove Add command for a ConnectionEthernet
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiConnectionEthernet_IsRemoveMessage(const CCNxControl *control);
+
+/**
+ * Creates an object from the control message
+ *
+ * The object does not carry any sense of Add or Remove, that is only part of the
+ * Control message.
+ *
+ * @param [in] control A CCNx Control message
+ *
+ * @return non-null An Allocated object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionEthernet *cpiConnectionEthernet_FromControl(const CCNxControl *control);
+
+/**
+ * Returns the interface name
+ *
+ * The caller should duplicate the string if it will be stored.
+ *
+ * @param [in] etherConn An allocated CPIConnectionEthernet
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiConnectionEthernet_GetInterfaceName(const CPIConnectionEthernet *etherConn);
+
+/**
+ * Returns the symbolic name
+ *
+ * The caller should duplicate the string if it will be stored.
+ *
+ * @param [in] etherConn An allocated CPIConnectionEthernet
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiConnectionEthernet_GetSymbolicName(const CPIConnectionEthernet *etherConn);
+
+/**
+ * Returns the peer link address
+ *
+ * Returns the peer's link address (e.g. 48-bit MAC address). The caller should
+ * acquire its own reference if he address will be stored externally to the
+ * CPIConnectionEthernet.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return non-null The peer's link address
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIAddress *cpiConnectionEthernet_GetPeerLinkAddress(const CPIConnectionEthernet *etherConn);
+
+/**
+ * Returns the ethertype to use
+ *
+ * The ethertype will be in host byte order.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+uint16_t cpiConnectionEthernet_GetEthertype(const CPIConnectionEthernet *etherConn);
+#endif // CCNx_Control_API_cpi_ConnectionEthernet_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.c b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.c
new file mode 100644
index 00000000..c4969fe9
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.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 <stdio.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_ArrayList.h>
+
+#include <ccnx/api/control/cpi_ConnectionList.h>
+#include <LongBow/runtime.h>
+
+struct cpi_connection_list {
+ PARCArrayList *listOfConnections;
+};
+
+/**
+ * PARCArrayList entry destroyer
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+static void
+_cpiConnectionList_ArrayDestroyer(void **voidPtr)
+{
+ CPIConnection **entryPtr = (CPIConnection **) voidPtr;
+ cpiConnection_Release(entryPtr);
+}
+
+CPIConnectionList *
+cpiConnectionList_Create()
+{
+ CPIConnectionList *list = parcMemory_AllocateAndClear(sizeof(CPIConnectionList));
+ assertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIConnectionList));
+ list->listOfConnections = parcArrayList_Create(_cpiConnectionList_ArrayDestroyer);
+ return list;
+}
+
+void
+cpiConnectionList_Destroy(CPIConnectionList **listPtr)
+{
+ assertNotNull(listPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*listPtr, "Parameter must dereference to non-null pointer");
+ CPIConnectionList *list = *listPtr;
+ parcArrayList_Destroy(&list->listOfConnections);
+ parcMemory_Deallocate((void **) &list);
+ *listPtr = NULL;
+}
+
+void
+cpiConnectionList_Append(CPIConnectionList *list, CPIConnection *entry)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ assertNotNull(entry, "Parameter entry must be non-null");
+
+ parcArrayList_Add(list->listOfConnections, entry);
+}
+
+size_t
+cpiConnectionList_Length(const CPIConnectionList *list)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ return parcArrayList_Size(list->listOfConnections);
+}
+
+CPIConnection *
+cpiConnectionList_Get(CPIConnectionList *list, size_t index)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ CPIConnection *original = (CPIConnection *) parcArrayList_Get(list->listOfConnections, index);
+ return cpiConnection_Copy(original);
+}
+
+bool
+cpiConnectionList_Equals(const CPIConnectionList *a, const CPIConnectionList *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (parcArrayList_Size(a->listOfConnections) == parcArrayList_Size(b->listOfConnections)) {
+ size_t length = parcArrayList_Size(a->listOfConnections);
+ for (size_t i = 0; i < length; i++) {
+ CPIConnection *tunnel_a = (CPIConnection *) parcArrayList_Get(a->listOfConnections, i);
+ CPIConnection *tunnel_b = (CPIConnection *) parcArrayList_Get(b->listOfConnections, i);
+ if (!cpiConnection_Equals(tunnel_a, tunnel_b)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+const char cpi_ConnectionList[] = "ConnectionList";
+
+PARCJSON *
+cpiConnectionList_ToJson(const CPIConnectionList *list)
+{
+ assertNotNull(list, "Parameter must be non-null");
+
+ PARCJSONArray *inner_json = parcJSONArray_Create();
+
+ size_t length = parcArrayList_Size(list->listOfConnections);
+ for (size_t i = 0; i < length; i++) {
+ CPIConnection *tunnel = (CPIConnection *) parcArrayList_Get(list->listOfConnections, i);
+ PARCJSON *json = cpiConnection_ToJson(tunnel);
+ PARCJSONValue *value = parcJSONValue_CreateFromJSON(json);
+ parcJSON_Release(&json);
+ parcJSONArray_AddValue(inner_json, value);
+ parcJSONValue_Release(&value);
+ }
+
+ PARCJSON *outter_json = parcJSON_Create();
+ parcJSON_AddArray(outter_json, cpi_ConnectionList, inner_json);
+ parcJSONArray_Release(&inner_json);
+ return outter_json;
+}
+
+CPIConnectionList *
+cpiConnectionList_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpi_ConnectionList);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpi_ConnectionList,
+ parcJSON_ToString(json));
+ PARCJSONArray *tunnelListJson = parcJSONValue_GetArray(value);
+
+ CPIConnectionList *list = cpiConnectionList_Create();
+
+ size_t length = parcJSONArray_GetLength(tunnelListJson);
+ for (size_t i = 0; i < length; i++) {
+ value = parcJSONArray_GetValue(tunnelListJson, i);
+ PARCJSON *tunnelJson = parcJSONValue_GetJSON(value);
+ CPIConnection *tunnel = cpiConnection_CreateFromJson(tunnelJson);
+ cpiConnectionList_Append(list, tunnel);
+ }
+
+ return list;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.h b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.h
new file mode 100644
index 00000000..6fdd4e64
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ConnectionList.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_ConnectionList.h
+ * @brief A list of CPIConnection objects
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_ConnectionList_h
+#define libccnx_cpi_ConnectionList_h
+
+struct cpi_connection_list;
+typedef struct cpi_connection_list CPIConnectionList;
+
+#include <ccnx/api/control/cpi_Connection.h>
+
+/**
+ * Creates an empty list of CPIConnection objects
+ *
+ * Each element in the list is reference counted, so the list may persist beyond
+ * what created it.
+ *
+ * @param <#param1#>
+ * @return An allocated list, you must call <code>cpiConnectionList_Destroy()</code>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionList *cpiConnectionList_Create(void);
+
+/**
+ * Destroys the list and all references in it
+ *
+ * Destroys each element in the list, which are reference counted. Only
+ * on the last destroy of each element is it actually freed.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiConnectionList_Destroy(CPIConnectionList **listPtr);
+
+/**
+ * Adds a iptunnel entry to the list.
+ *
+ * Appends <code>entry</code> to the list. Takes ownership of the entry
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiConnectionList_Append(CPIConnectionList *list, CPIConnection *entry);
+
+/**
+ * Determine if two CPIConnectionList instances are equal.
+ *
+ * Two CPIConnectionList instances are equal if, and only if,they have the same number of objects and
+ * the objects -- in order -- are equal.
+ *
+ * The following equivalence relations on non-null `CPIConnectionList` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIConnectionList_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiConnectionList_Equals(x, y)` must return true if and only if
+ * `cpiConnectionList_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiConnectionList_Equals(x, y)` returns true and
+ * `cpiConnectionList_Equals(y, z)` returns true,
+ * then `cpiConnectionList_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiConnectionList_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiConnectionList_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIConnectionList` instance.
+ * @param b A pointer to a `CPIConnectionList` instance.
+ * @return true if the two `CPIConnectionList` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIConnectionList *a = cpiConnectionList_Create();
+ * CPIConnectionList *b = cpiConnectionList_Create();
+ *
+ * if (cpiConnectionList_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiConnectionList_Equals(const CPIConnectionList *a, const CPIConnectionList *b);
+
+/**
+ * The number of elements in the list
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return The number of elements in the list.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t cpiConnectionList_Length(const CPIConnectionList *list);
+
+/**
+ * Returns a reference counted copy of the iptunnel entry.
+ *
+ * Caller must destroy the returned value.
+ * Will assert if you go beyond the end of the list.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnection *cpiConnectionList_Get(CPIConnectionList *list, size_t index);
+
+/**
+ * A JSON representation of the list
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiConnectionList_ToJson(const CPIConnectionList *list);
+
+/**
+ * Constructs a list
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated list based on the JSON
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionList *cpiConnectionList_FromJson(PARCJSON *json);
+#endif // libccnx_cpi_ConnectionList_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.c b/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.c
new file mode 100644
index 00000000..a0c2e7da
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/api/control/cpi_ControlFacade.h>
+#include <ccnx/common/codec/schema_v1/ccnxCodecSchemaV1_TlvDictionary.h>
+
+#include <ccnx/common/ccnx_Name.h>
+
+static const char _NotificationIndicator[] = "notificationWrapper";
+static const char _NotificationPayload[] = "notificationPayload";
+
+
+// ===========================================================================================================
+
+
+CCNxControl *
+ccnxControlFacade_CreateCPI(PARCJSON *ccnx_json)
+{
+ assertNotNull(ccnx_json, "Parameter ccnx_json must be non-null");
+
+ CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateControl();
+
+ ccnxTlvDictionary_PutJson(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, ccnx_json);
+
+ return dictionary;
+}
+
+CCNxControl *
+ccnxControlFacade_CreateNotification(PARCJSON *payload)
+{
+ assertNotNull(payload, "Parameter ccnx_json must be non-null");
+
+ CCNxTlvDictionary *dictionary = ccnxCodecSchemaV1TlvDictionary_CreateControl();
+
+ // Create a new JSON object that indicates that this is a notification. Wrap it around
+ // the supplied JSON object.
+
+ PARCJSON *notificationWrapper = parcJSON_Create();
+ parcJSON_AddBoolean(notificationWrapper, _NotificationIndicator, true);
+ parcJSON_AddObject(notificationWrapper, _NotificationPayload, payload);
+ ccnxTlvDictionary_PutJson(dictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD, notificationWrapper);
+ parcJSON_Release(&notificationWrapper);
+
+ return dictionary;
+}
+
+PARCJSON *
+ccnxControlFacade_GetJson(const CCNxTlvDictionary *controlDictionary)
+{
+ ccnxControlFacade_AssertValid(controlDictionary);
+ PARCJSON *controlJSON = ccnxTlvDictionary_GetJson(controlDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD);
+
+ if (ccnxControlFacade_IsNotification(controlDictionary)) {
+ PARCJSONValue *wrappedJSON = parcJSON_GetValueByName(controlJSON, _NotificationPayload);
+ controlJSON = parcJSONValue_GetJSON(wrappedJSON);
+ }
+
+ return controlJSON;
+}
+
+bool
+ccnxControlFacade_IsCPI(const CCNxTlvDictionary *controlDictionary)
+{
+ bool result = false;
+ ccnxControlFacade_AssertValid(controlDictionary);
+
+ result = ccnxTlvDictionary_IsControl(controlDictionary);
+
+ PARCJSON *controlJSON = ccnxTlvDictionary_GetJson(controlDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD);
+ if (controlJSON != NULL) {
+ if (parcJSON_GetValueByName(controlJSON, _NotificationIndicator) != NULL) {
+ // this is a notification
+ result = false;
+ }
+ }
+ return result;
+}
+
+bool
+ccnxControlFacade_IsNotification(const CCNxTlvDictionary *controlDictionary)
+{
+ bool result = false;
+
+ ccnxControlFacade_AssertValid(controlDictionary);
+
+ PARCJSON *controlJSON = ccnxTlvDictionary_GetJson(controlDictionary, CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD);
+ if (controlJSON != NULL && (parcJSON_GetValueByName(controlJSON, _NotificationIndicator) != NULL)) {
+ result = true;
+ }
+ return result;
+}
+
+void
+ccnxControlFacade_Display(const CCNxTlvDictionary *contentDictionary, int indentation)
+{
+ ccnxTlvDictionary_Display(contentDictionary, indentation);
+}
+
+char *
+ccnxControlFacade_ToString(const CCNxTlvDictionary *contentDictionary)
+{
+ char *string;
+ char *jsonString = NULL;
+
+ PARCJSON *json = ccnxControlFacade_GetJson(contentDictionary);
+ if (json != NULL) {
+ jsonString = parcJSON_ToString(json);
+ }
+
+ int failure = asprintf(&string, "CCNxControl { isCPI=%s, isNotification=%s, JSON=\"%s\"}",
+ ccnxControlFacade_IsCPI(contentDictionary) ? "true" : "false",
+ ccnxControlFacade_IsNotification(contentDictionary) ? "true" : "false",
+ jsonString != NULL ? jsonString : "NULL");
+
+
+ if (jsonString) {
+ parcMemory_Deallocate((void **) &jsonString);
+ }
+
+ assertTrue(failure > -1, "Error asprintf");
+
+ char *result = parcMemory_StringDuplicate(string, strlen(string));
+ free(string);
+
+ return result;
+}
+
+void
+ccnxControlFacade_AssertValid(const CCNxTlvDictionary *controlDictionary)
+{
+ assertNotNull(controlDictionary, "Parameter must be a non-null CCNxControlFacade pointer");
+
+
+ assertTrue(ccnxTlvDictionary_IsValueJson(controlDictionary,
+ CCNxCodecSchemaV1TlvDictionary_MessageFastArray_PAYLOAD), "Does not have JSON payload");
+ assertTrue(ccnxTlvDictionary_IsControl(controlDictionary), "Does not have type set");
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.h b/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.h
new file mode 100644
index 00000000..26d84f22
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ControlFacade.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_ControlFacade.h
+ * @ingroup Utility
+ * @brief <#Brief Description#>
+ *
+ * A ControlFacade has several flavors. A Notification is a spontaneous message
+ * sent as an indication of some state or condition. A CPI (ControlPlaneInterface)
+ * message is a Request/Reponse protocol used to manage the Transport.
+ *
+ * The ccnxControlFacade takes ownership of the JSON object
+ * and will destroy it when it's Destroy is called.
+ *
+ * If put inside a CCNxMetaMessage and sent to the Transport, the Transport
+ * takes ownership of the CCNxMetaMessage and will thus be responsible for
+ * destroying the object.
+ *
+ */
+#ifndef libccnx_ccnx_ControlFacade_h
+#define libccnx_ccnx_ControlFacade_h
+
+#include <ccnx/common/ccnx_Name.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+typedef enum {
+ CCNxControlMessage_Unknown = 0,
+ CCNxControlMessage_CPI = 1,
+ CCNxControlMessage_Notify = 2
+} CCNxControlFacadeType;
+
+// =====================
+
+/**
+ * Creates a Nofification control message from the supplied JSON object.
+ *
+ * The newly created instance must eventually be released by calling
+ * {@link ccnxControl_Release}.
+ *
+ * @param ccnxJson the JSON object to include in the message.
+ * @return A `CCNxControl` message.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *ccnxControlFacade_CreateNotification(PARCJSON *ccnxJson);
+
+/**
+ * Creates a CPI message from the supplied JSON object.
+ *
+ * The newly created instance must eventually be released by calling
+ * {@link ccnxControl_Release}.
+ *
+ * @param ccnxJson the JSON to include with the message.
+ * @return A `CCNxControl` message.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *ccnxControlFacade_CreateCPI(PARCJSON *ccnxJson);
+
+// =====================
+// Getters
+
+/**
+ * Return a pointer to the JSON object contained in the control message.
+ *
+ * <#Discussion#>
+ *
+ * @param controlDictionary the control message to retrieve the JSON from.
+ * @return the PARCJSON object from the control message.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *ccnxControlFacade_GetJson(const CCNxTlvDictionary *controlDictionary);
+
+/**
+ * Test whether a control message is a Notification.
+ *
+ * <#Discussion#>
+ *
+ * @param controlDictionary the control message to test.
+ * @return true if the control message is a Notification.
+ * @return false if the control message is not a Notification.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool ccnxControlFacade_IsNotification(const CCNxTlvDictionary *controlDictionary);
+
+/**
+ * Test whether a control message is a CPI (Control Plane Interface) message.
+ *
+ * <#Discussion#>
+ *
+ * @param controlDictionary the control message to test.
+ * @return true if the control message is a CPI message.
+ * @return false if the control message is not a CPI message.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool ccnxControlFacade_IsCPI(const CCNxTlvDictionary *controlDictionary);
+
+
+// =====================
+// Miscellaneous
+
+/**
+ * Print a human readable representation of the given `CCNxTlvDictionary` representing
+ * a control message.
+ *
+ * @param [in] name A pointer to the `CCNxTlvDictionary` instance representing a CCNxControl.
+ * @param [in] indentation The level of indentation to use to pretty-print the output.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxTlvDictionary *controlMessage = ccnxControlFacade_CreateCPI(...);
+ * ccnxControlFacade_Display(controlMessage);
+ * ...
+ * }
+ * @endcode
+ */
+void ccnxControlFacade_Display(const CCNxTlvDictionary *controlDictionary, int indentation);
+
+/**
+ * Produce a null-terminated string representation of the specified CCNxTlvDictionary instance
+ * representing a control message.
+ *
+ * The non-null result must be freed by the caller via {@link parcMemory_Deallocate}.
+ *
+ * @param [in] name A pointer to the `CCNxTlvDictionary` instance representing a CCNxControl.
+ *
+ * @return NULL Cannot allocate memory.
+ * @return non-NULL A pointer to an allocated,
+ * null-terminated C string that must be deallocated via `parcMemory_Deallocate()`.
+ *
+ * Example:
+ * @code
+ * {
+ * char *desc = ccnxControlFacade_ToString(controlDictionary);
+ * printf("%s\n", desc);
+ * parcMemory_Deallocate((void **) &desc);
+ *
+ * ccnxTlvDictionary_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControlFacade_Display}
+ * @see {@link parcMemory_Deallocate}
+ */
+char *ccnxControlFacade_ToString(const CCNxTlvDictionary *controlDictionary);
+
+/**
+ * Assert that an instance of `CCNxTlvDictionary` is a valid control message.
+ *
+ * If the instance is not valid, terminate via {@link trapIllegalValue}
+ *
+ * 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] name A pointer to a `CCNxName` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *controlMessage = ccnxControlFacade_CreateCPI(...);
+ * ccnxControlFacade_AssertValid(controlMessage);
+ * ...
+ * }
+ * @endcode
+ */
+void ccnxControlFacade_AssertValid(const CCNxTlvDictionary *controlDictionary);
+#endif // libccnx_ccnx_ControlFacade_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.c b/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.c
new file mode 100644
index 00000000..3150c2d3
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/api/control/cpi_ControlMessage.h>
+
+#include <ccnx/api/control/cpi_ControlFacade.h>
+
+#include <ccnx/api/notify/notify_Status.h>
+
+#include <ccnx/api/control/controlPlaneInterface.h>
+
+PARCJSON *
+ccnxControl_GetJson(const CCNxControl *control)
+{
+ return ccnxControlFacade_GetJson(control);
+}
+
+void
+ccnxControl_Display(const CCNxControl *control, int indentation)
+{
+ ccnxControlFacade_Display(control, indentation);
+}
+
+void
+ccnxControl_Release(CCNxControl **controlP)
+{
+ ccnxTlvDictionary_Release(controlP);
+}
+
+CCNxControl *
+ccnxControl_Acquire(const CCNxControl *control)
+{
+ return ccnxTlvDictionary_Acquire(control);
+}
+
+bool
+ccnxControl_IsACK(const CCNxControl *control)
+{
+ if (cpi_GetMessageType(control) == CPI_ACK) {
+ PARCJSON *json = ccnxControlFacade_GetJson(control);
+ return cpiAcks_IsAck(json);
+ }
+ return false;
+}
+
+bool
+ccnxControl_IsNACK(const CCNxControl *control)
+{
+ if (cpi_GetMessageType(control) == CPI_ACK) {
+ PARCJSON *json = ccnxControlFacade_GetJson(control);
+ return !cpiAcks_IsAck(json);
+ }
+ return false;
+}
+
+uint64_t
+ccnxControl_GetAckOriginalSequenceNumber(const CCNxControl *control)
+{
+ PARCJSON *json = ccnxControlFacade_GetJson(control);
+ return cpiAcks_GetAckOriginalSequenceNumber(json);
+}
+
+bool
+ccnxControl_IsNotification(const CCNxControl *control)
+{
+ return ccnxControlFacade_IsNotification(control);
+}
+
+NotifyStatus *
+ccnxControl_GetNotifyStatus(const CCNxControl *control)
+{
+ return notifyStatus_ParseJSON(ccnxControl_GetJson(control));
+}
+
+CCNxControl *
+ccnxControl_CreateCPIRequest(PARCJSON *json)
+{
+ return ccnxControlFacade_CreateCPI(json);
+}
+
+CCNxControl *
+ccnxControl_CreateAddRouteRequest(const CPIRouteEntry *route)
+{
+ PARCJSON *cpiRequest = cpiForwarding_CreateAddRouteRequest(route);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateRemoveRouteRequest(const CPIRouteEntry *route)
+{
+ PARCJSON *cpiRequest = cpiForwarding_CreateRemoveRouteRequest(route);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateSetStrategyRequest(const CPIForwardingStrategy *fwdStrategy)
+{
+ PARCJSON *cpiRequest = cpiForwarding_CreateSetStrategyRequest(fwdStrategy);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateSetWldrRequest(const CPIManageWldr *cpiWldr)
+{
+ PARCJSON *cpiRequest = cpiLinks_CreateSetWldrRequest(cpiWldr);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateRouteListRequest()
+{
+ PARCJSON *cpiRequest = cpiForwarding_CreateRouteListRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateConnectionListRequest()
+{
+ PARCJSON *cpiRequest = cpiLinks_CreateConnectionListRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateInterfaceListRequest()
+{
+ PARCJSON *cpiRequest = cpiLinks_CreateInterfaceListRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateAddRouteToSelfRequest(const CCNxName *name)
+{
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ CCNxControl *result = ccnxControl_CreateAddRouteRequest(route);
+ cpiRouteEntry_Destroy(&route);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateRemoveRouteToSelfRequest(const CCNxName *name)
+{
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ CCNxControl *result = ccnxControl_CreateRemoveRouteRequest(route);
+ cpiRouteEntry_Destroy(&route);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreatePauseInputRequest()
+{
+ PARCJSON *cpiRequest = cpi_CreatePauseInputRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateFlushRequest(void)
+{
+ PARCJSON *cpiRequest = cpi_CreateFlushRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+bool
+ccnxControl_IsCPI(const CCNxControl *controlMsg)
+{
+ return ccnxControlFacade_IsCPI((CCNxTlvDictionary *) controlMsg);
+}
+
+CCNxControl *
+ccnxControl_CreateIPTunnelRequest(const CPIInterfaceIPTunnel *tunnel)
+{
+ PARCJSON *request = cpiLinks_CreateIPTunnel(tunnel);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(request);
+ parcJSON_Release(&request);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateCancelFlowRequest(const CCNxName *name)
+{
+ PARCJSON *request = cpiCancelFlow_CreateRequest(name);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(request);
+ parcJSON_Release(&request);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateCacheStoreRequest(bool activate)
+{
+ PARCJSON *cpiRequest = cpiManageChaces_CreateCacheStoreRequest(activate);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateCacheServeRequest(bool activate)
+{
+ PARCJSON *cpiRequest = cpiManageChaces_CreateCacheServeRequest(activate);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
+
+CCNxControl *
+ccnxControl_CreateCacheClearRequest()
+{
+ PARCJSON *cpiRequest = cpiManageChaces_CreateCacheClearRequest();
+ CCNxControl *result = ccnxControl_CreateCPIRequest(cpiRequest);
+ parcJSON_Release(&cpiRequest);
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.h b/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.h
new file mode 100644
index 00000000..84f085e7
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ControlMessage.h
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_Control.h
+ * @brief This is a stack control message.
+ *
+ * This may induce other Control messages for the stack, for the Forwarder, or potentially
+ * for the network.
+ *
+ */
+
+#ifndef libccnx_ccnx_Control_h
+#define libccnx_ccnx_Control_h
+
+#include <stdint.h>
+
+#include <ccnx/common/internal/ccnx_TlvDictionary.h>
+
+#include <ccnx/api/notify/notify_Status.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/api/control/cpi_ForwardingStrategy.h>
+#include <ccnx/api/control/cpi_ManageWldr.h>
+#include <ccnx/api/control/cpi_InterfaceIPTunnel.h>
+
+/**
+ * @typedef CCNxControl
+ * @brief Control message for CCNx.
+ */
+typedef CCNxTlvDictionary CCNxControl;
+
+/**
+ * Increase the number of references to a `CCNxControl` instance.
+ *
+ * Note that new `CCNxControl` is not created,
+ * only that the given `CCNxControl` reference count is incremented.
+ * Discard the reference by invoking {@link ccnxControl_Release()}.
+ *
+ * @param [in] control A pointer to the original instance.
+ * @return The value of the input parameter @p control.
+ *
+ * Example:
+ * @code
+ * {
+ * ...
+ *
+ * CCNxControl *control = ccnxControl_Acquire(instance);
+ *
+ * ccnxControl_Release(&control);
+ *
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_Acquire(const CCNxControl *control);
+
+/**
+ * Print a human readable representation of the given `CCNxControl` instance.
+ *
+ * @param [in] indentation The level of indentation to use to pretty-print the output.
+ * @param [in] control A pointer to the instance to display.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = < ... >
+ *
+ * ccnxControl_Display(control, 4);
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ */
+void ccnxControl_Display(const CCNxControl *control, int indentation);
+
+/**
+ * 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] controlP A pointer to a pointer to the instance to release.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = < ... >
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Acquire}
+ */
+void ccnxControl_Release(CCNxControl **controlP);
+
+/**
+ * Return the original sequence number to which an ACK corresponds.
+ *
+ * Control plane messages contain sequence numbers. When an ACK is received, this function
+ * returns the sequence number of the control plane message being ACKed.
+ *
+ * @param [in] control A pointer to a `CCNxControl` instance.
+ *
+ * @return The sequence number of the control plane message being ACKed.
+ *
+ * Example:
+ * @code
+ * {
+ * uint64_t originalSequenceNumber = ccnxControl_GetAckOriginalSequenceNumber(control);
+ * }
+ * @endcode
+ */
+uint64_t ccnxControl_GetAckOriginalSequenceNumber(const CCNxControl *control);
+
+/**
+ * Return true if the specified `CCNxControl` instance is a Notification.
+ *
+ * @param [in] control A pointer to a `CCNxControl` instance.
+ *
+ * @return `true` if the specified `CCNxControl` instance is a Notification message.
+ * @return `false` if the specified `CCNxControl` instance is not a Notification message.
+ *
+ * Example:
+ * @code
+ * {
+ * bool isNotification = ccnxControl_IsNotification(control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_IsACK}
+ */
+bool ccnxControl_IsNotification(const CCNxControl *control);
+
+/**
+ * Return `true` if the specified `CCNxControl` instance is an ACK message carrying an ACK (not a NACK)
+ *
+ * An acknolwedgement message can be either a positive (ACK) or negative (NACK) acknowlegement.
+ * In both cases, it carries the original sequence number of the message being ACKed or NACKed.
+ *
+ * @param [in] control A pointer to a `CCNxControl` instance.
+ *
+ * @return `true` if the specified `CCNxControl` instance is an Ack message.
+ * @return `false` if the specified `CCNxControl` instance is not an Ack message.
+ *
+ * Example:
+ * @code
+ * {
+ * bool isAck = ccnxControl_IsACK(control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_IsNotification}
+ */
+bool ccnxControl_IsACK(const CCNxControl *control);
+
+/**
+ * Return `true` if the specified `CCNxControl` instance is an ACK message carrying a NACK (not a ACK)
+ *
+ * An acknolwedgement message can be either a positive (ACK) or negative (NACK) acknowlegement.
+ * In both cases, it carries the original sequence number of the message being ACKed or NACKed.
+ *
+ * @param [in] control A pointer to a `CCNxControl` instance.
+ *
+ * @return `true` if the specified `CCNxControl` is an NACK.
+ * @return `false` if the specified `CCNxControl` instance is not an NAck message.
+ *
+ * Example:
+ * @code
+ * {
+ * bool isAck = ccnxControl_IsACK(control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_IsNotification}
+ */
+bool ccnxControl_IsNACK(const CCNxControl *control);
+
+/**
+ * Get the {@link NotifyStatus} from a `CCNxControl` instance, if it exists.
+ *
+ * This function creates a new instance of `NotifyStatus`, initialized from the specified
+ * `CCNxControl`, which must eventually be released by calling {@link notifyStatus_Release}().
+ * If the specified `CCNxControl` instance does not contain a `NotifyStatus`, this function will return NULL.
+ *
+ * @param [in] control A pointer to the instance of `CCNxControl` from which to retrieve the `NotifyStatus`.
+ *
+ * @return An instance of `NotifyStatus`, if it exists.
+ * @return NULL If the `CCNxControl` instance did not contain a `NotifyStatus`.
+ *
+ * Example:
+ * @code
+ * {
+ * NotifyStatus status = ccnxControl_GetNotifyStatus(control);
+ *
+ * notifyStatus_Release(&status);
+ * }
+ * @endcode
+ *
+ * @see {@link notifyStatus_Release}
+ * @see {@link NotifyStatus}
+ */
+NotifyStatus *ccnxControl_GetNotifyStatus(const CCNxControl *control);
+
+/**
+ * Create a new `CCNxControl` instance containing a request to add a route to the control plane.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release()}.
+ *
+ * @param [in] route The {@link CPIRouteEntry} to add.
+ *
+ * @return A new `CCNxControl` instance encapsulating a request to add the specified route.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIRouteEntry *cpiRouteEntry = cpiRouteEntry_Create(...);
+ *
+ * CCNxControl *control = ccnxControl_CreateAddRouteRequest(cpiRouteEntry);
+ *
+ * cpiRouteEntry_Destroy(&cpiRouteEntry);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_CreateRemoveRouteRequest}
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateAddRouteRequest(const CPIRouteEntry *route);
+
+/**
+ * Create a new `CCNxControl` instance containing a request to remove a route from the control plane.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @param [in] route The {@link CPIRouteEntry} to remove.
+ *
+ * @return A new `CCNxControl` instance encapsulating a request to remove the specified route.
+ *
+ * Example:
+ * @code
+ * {
+ * {
+ * CPIRouteEntry *cpiRouteEntry = cpiRouteEntry_Create(...);
+ *
+ * CCNxControl *control = ccnxControl_CreateRemoveRouteRequest(cpiRouteEntry);
+ *
+ * cpiRouteEntry_Destroy(&cpiRouteEntry);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_CreateAddRouteRequest}
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateRemoveRouteRequest(const CPIRouteEntry *route);
+
+CCNxControl *ccnxControl_CreateSetStrategyRequest(const CPIForwardingStrategy *fwdStrategy);
+
+CCNxControl *ccnxControl_CreateSetWldrRequest(const CPIManageWldr *cpiWldr);
+
+/**
+ * Create a new `CCNxControl` instance containing a request to add a route for CCN messages matching the given {@link CCNxName}
+ * back to the caller's network interface.
+ *
+ * The created `CCNxControl` message describes to the forwarder that messages matching the specified `CCNxName` should be
+ * routed back to the caller. This is how to initiate listening for a name.
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @param [in] name A pointer to a `CCNxName` instance containing the name to match against.
+ *
+ * @return A new `CCNxControl` instance encapsulating a request to add a route for the given `CCNxName` back to the caller's
+ * network interface.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/thingie");
+ *
+ * CCNxControl *control = ccnxControl_CreateAddRouteToSelfRequest(name);
+ * ...
+ *
+ * ccnxName_Release(&name);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_CreateRemoveRouteToSelfRequest}
+ * @see {@link ccnxControl_CreateAddRouteRequest}
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateAddRouteToSelfRequest(const CCNxName *name);
+
+/**
+ * Create a new `CCNxControl` instance containing a request to remove a route to the caller for messages matching the specified
+ * {@link CCNxName}.
+ *
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @param [in] name A pointer to a `CCNxName` instance containing the name to match against.
+ *
+ * @return A new `CCNxControl` instance encapsulating a request to remove the specified route.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/parc/csl/media/thingie");
+ *
+ * CCNxControl *control = ccnxControl_CreateRemoveRouteToSelfRequest(name);
+ * ...
+ *
+ * ccnxName_Release(&name);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_CreateAddRouteToSelfRequest}
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateRemoveRouteToSelfRequest(const CCNxName *name);
+
+/**
+ * Create a new `CCNxControl` instance containing the specified CPI command, and including the
+ * flag indicating that it is a CPI message.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @param [in] json A pointer to a {@link PARCJSON} instance containing CPI command to wrap.
+ *
+ * @return A new `CCNxControl` instance containing the specified CPI command.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ * PARCJSON *cpiRequest = cpiCancelFlow_CreateRequest(name);
+ * CCNxControl *control = ccnxControl_CreateCPIRequest(cpiRequest);
+ *
+ * ...
+ *
+ * parcJSON_Release(&cpiRequest);
+ * ccnxControl_Release(&control);
+ * ccnxName_Release(&name);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateCPIRequest(PARCJSON *json);
+
+/**
+ * Create a new `CCNxControl` instance containing a "List Routes" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ * PARCJSON *json = ccnxControl_GetJson(control);
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateRouteListRequest(void);
+
+/**
+ * Create a new `CCNxControl` instance containing a "List Connections" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateConnectionListRequest(void);
+
+/**
+ * Create a new `CCNxControl` instance containing a "List Interfaces" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = ccnxControl_CreateInterfaceListRequest();
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateInterfaceListRequest(void);
+
+/**
+ * Create a new `CCNxControl` instance containing a "Pause Input" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = ccnxControl_CreatePauseInputRequest();
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ * @see {@link cpi_CreatePauseInputRequest}
+ */
+CCNxControl *ccnxControl_CreatePauseInputRequest(void);
+
+
+/**
+ * Creates a request to flush the output. The ForwarderConnector will ACK the request.
+ *
+ * When the user recieves an ACK with the corresponding sequence number as this request, the
+ * user knows that all ouptut prior to that request has been processed.
+ *
+ * @retval non-null An allocated CCnxControl message
+ * @retval null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *ccnxControl_CreateFlushRequest(void);
+
+/**
+ * Create a new `CCNxControl` instance containing a "Cancel Flow" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ * @param [in] name A pointer to a `CCNxName`.
+ *
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ * CCNxControl *control = ccnxControl_CreateCancelFlowRequest(name);
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * ccnxName_Release(&name);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateCancelFlowRequest(const CCNxName *name);
+
+/**
+ * Create a new `CCNxControl` instance containing a "Create IP Tunnel" request.
+ *
+ * The new `CCNxControl` instance must eventually be released by calling {@link ccnxControl_Release}.
+ *
+ * @param [in] tunnel An instance of `CPIInterfaceIPTunnel` to be included.
+ * @return A new `CCNxControl` instance containing the request.
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in sockaddr_any;
+ * memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ * sockaddr_any.sin_family = PF_INET;
+ * sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+ *
+ * CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+ *
+ * struct sockaddr_in sockaddr_dst;
+ * memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ * sockaddr_dst.sin_family = PF_INET;
+ * sockaddr_dst.sin_port = htons(9999);
+ * inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+ *
+ * CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+ *
+ * CPIInterfaceIPTunnel *tunnel = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP);
+ * CCNxControl *control = ccnxControl_CreateIPTunnelRequest(tunnel);
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * cpiInterfaceIPTunnel_Destroy(&tunnel);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_Release}
+ */
+CCNxControl *ccnxControl_CreateIPTunnelRequest(const CPIInterfaceIPTunnel *tunnel);
+
+
+CCNxControl *ccnxControl_CreateCacheStoreRequest(bool activate);
+CCNxControl *ccnxControl_CreateCacheServeRequest(bool activate);
+CCNxControl *ccnxControl_CreateCacheClearRequest();
+
+/**
+ * Return true if the specified `CCNxControl` instance is an a CPI request.
+ *
+ * @param [in] controlMsg A pointer to a `CCNxControl` instance.
+ *
+ * @return `true` if the specified `CCNxControl` instance is a CPI request.
+ * @return `false` if the specified `CCNxControl` instance is not a CPI request.
+ *
+ * Example:
+ * @code
+ * {
+ * bool isCPI = ccnxControl_IsCPI(control);
+ * }
+ * @endcode
+ *
+ * @see {@link ccnxControl_IsNotification}
+ */
+bool ccnxControl_IsCPI(const CCNxControl *controlMsg);
+
+/**
+ * Return the underlying CPI request from the specified `CCNxControl`.
+ *
+ * @return A pointer to the underlying CPI request object.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ * PARCJSON *json = ccnxControl_GetJson(control);
+ *
+ * ...
+ *
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ *
+ * @see ccnxControl_Release
+ */
+PARCJSON *ccnxControl_GetJson(const CCNxControl *control);
+#endif // libccnx_ccnx_Control_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.c b/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.c
new file mode 100644
index 00000000..3cd836d2
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/controlPlaneInterface.h>
+#include <ccnx/api/control/cpi_Forwarding.h>
+#include <LongBow/runtime.h>
+
+#include "cpi_private.h"
+
+static const char *cpiRegister = "REGISTER";
+static const char *cpiUnregister = "UNREGISTER";
+static const char *cpiRouteList = "ROUTE_LIST";
+static const char *cpiSetStrategy = "SET_STRATEGY";
+
+PARCJSON *
+cpiForwarding_CreateSetStrategyRequest(CPIForwardingStrategy *fwdStrategy)
+{
+ PARCJSON *json = cpiForwardingStrategy_ToJson(fwdStrategy);
+ PARCJSON *result = cpi_CreateRequest(cpiSetStrategy, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_CreateAddRouteRequest(const CPIRouteEntry *route)
+{
+ PARCJSON *routeAsJSON = cpiRouteEntry_ToJson(route);
+ PARCJSON *result = cpi_CreateRequest(cpiRegister, routeAsJSON);
+ parcJSON_Release(&routeAsJSON);
+
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_CreateRemoveRouteRequest(const CPIRouteEntry *route)
+{
+ PARCJSON *routeAsJSON = cpiRouteEntry_ToJson(route);
+ PARCJSON *result = cpi_CreateRequest(cpiUnregister, routeAsJSON);
+ parcJSON_Release(&routeAsJSON);
+
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_AddRouteToSelf(const CCNxName *prefix)
+{
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(prefix);
+ PARCJSON *result = cpiForwarding_AddRoute(route);
+ cpiRouteEntry_Destroy(&route);
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_RemoveRouteToSelf(const CCNxName *prefix)
+{
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(prefix);
+ PARCJSON *result = cpiForwarding_RemoveRoute(route);
+ cpiRouteEntry_Destroy(&route);
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_AddRoute(const CPIRouteEntry *route)
+{
+ PARCJSON *operation = cpiRouteEntry_ToJson(route);
+ PARCJSON *result = cpi_CreateRequest(cpiRegister, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+PARCJSON *
+cpiForwarding_RemoveRoute(const CPIRouteEntry *route)
+{
+ PARCJSON *operation = cpiRouteEntry_ToJson(route);
+ PARCJSON *result = cpi_CreateRequest(cpiUnregister, operation);
+ parcJSON_Release(&operation);
+
+ return result;
+}
+
+CPIRouteEntry *
+cpiForwarding_RouteFromControlMessage(CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ PARCJSON *json = ccnxControl_GetJson(control);
+
+ PARCJSONPair *routeOpPair = cpi_ParseRequest(json);
+ PARCJSON *routeJson = parcJSONValue_GetJSON(parcJSONPair_GetValue(routeOpPair));
+
+ CPIRouteEntry *route = cpiRouteEntry_FromJson(routeJson);
+
+ return route;
+}
+
+CPIForwardingStrategy *
+cpiForwarding_ForwardingStrategyFromControlMessage(CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ PARCJSON *json = ccnxControl_GetJson(control);
+
+ PARCJSONPair *fwdStrOpPair = cpi_ParseRequest(json);
+ PARCJSON *fwdStrategyJson = parcJSONValue_GetJSON(parcJSONPair_GetValue(fwdStrOpPair));
+ CPIForwardingStrategy *fwdStrategy = cpiForwardingStrategy_FromJson(fwdStrategyJson);
+
+ return fwdStrategy;
+}
+
+const char *
+cpiForwarding_AddRouteJsonTag()
+{
+ return cpiRegister;
+}
+
+const char *
+cpiForwarding_RemoveRouteJsonTag()
+{
+ return cpiUnregister;
+}
+
+const char *
+cpiForwarding_RouteListJsonTag()
+{
+ return cpiRouteList;
+}
+
+const char *
+cpiForwarding_SetStrategyJsonTag()
+{
+ return cpiSetStrategy;
+}
+
+
+PARCJSON *
+cpiForwarding_CreateRouteListRequest()
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiRouteList, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+CPIRouteEntryList *
+cpiForwarding_RouteListFromControlMessage(CCNxControl *control)
+{
+ PARCJSON *json = ccnxControl_GetJson(control);
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiRequest_GetJsonTag());
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(json, cpiResponse_GetJsonTag());
+ }
+ PARCJSON *innerJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(innerJson, cpiRouteList);
+ PARCJSON *operation = parcJSONValue_GetJSON(value);
+ return cpiRouteEntryList_FromJson(operation);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.h b/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.h
new file mode 100644
index 00000000..258939fd
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Forwarding.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Forwarding.h
+ * @brief CPI Forwarding
+ *
+ */
+#ifndef libccnx_cpi_ManageForwarding_h
+#define libccnx_cpi_ManageForwarding_h
+
+#include <ccnx/common/ccnx_Name.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_NameRouteType.h>
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/api/control/cpi_RouteEntryList.h>
+#include <ccnx/api/control/cpi_ForwardingStrategy.h>
+
+/**
+ * Generate a request for a list all routes
+ *
+ * The transport should resond with a CPI Response message.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiForwarding_CreateRouteListRequest();
+
+/**
+ * Parse a control message into a list of interfaces
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIRouteEntryList *cpiForwarding_RouteListFromControlMessage(CCNxControl *response);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiForwarding_CreateAddRouteRequest(const CPIRouteEntry *route);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiForwarding_CreateRemoveRouteRequest(const CPIRouteEntry *route);
+
+/**
+ * Simplified form of <code>cpiForwarding_AddRoute</code> to add a route to the current transport
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiForwarding_AddRouteToSelf(const CCNxName *prefix);
+
+/**
+ * Simplified form of <code>cpiForwarding_RemoveRoute</code> to remove a route to the current transport
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiForwarding_RemoveRouteToSelf(const CCNxName *prefix);
+
+/**
+ * Creates a control message representing the route
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiForwarding_AddRoute(const CPIRouteEntry *route);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiForwarding_RemoveRoute(const CPIRouteEntry *route);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIRouteEntry *cpiForwarding_RouteFromControlMessage(CCNxControl *control);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const char *cpiForwarding_AddRouteJsonTag();
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const char *cpiForwarding_RemoveRouteJsonTag();
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const char *cpiForwarding_RouteListJsonTag();
+
+PARCJSON *cpiForwarding_CreateSetStrategyRequest();
+const char *cpiForwarding_SetStrategyJsonTag();
+CPIForwardingStrategy *cpiForwarding_ForwardingStrategyFromControlMessage(CCNxControl *control);
+#endif // libccnx_cpi_ManageForwarding_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.c b/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.c
new file mode 100644
index 00000000..89cce66f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdlib.h>
+#include <sys/time.h>
+
+#include <ccnx/api/control/cpi_ForwardingStrategy.h>
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <limits.h>
+
+#include <LongBow/runtime.h>
+
+static const char *cpiPrefix = "PREFIX";
+static const char *cpiStrategy = "STRATEGY";
+
+struct cpi_forwarding_strategy {
+ CCNxName *prefix;
+ char *strategy;
+};
+
+void
+cpiForwardingStrategy_Destroy(CPIForwardingStrategy **fwdStrategyPtr)
+{
+ assertNotNull(fwdStrategyPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*fwdStrategyPtr, "Parameter must dereference to non-null pointer");
+ CPIForwardingStrategy *fwdStrategy = *fwdStrategyPtr;
+
+ ccnxName_Release(&fwdStrategy->prefix);
+ parcMemory_Deallocate((void **) &fwdStrategy->strategy);
+
+ parcMemory_Deallocate((void **) &fwdStrategy);
+ *fwdStrategyPtr = NULL;
+}
+
+CPIForwardingStrategy *
+cpiForwardingStrategy_Create(CCNxName *prefix, char *strategy)
+{
+ CPIForwardingStrategy *fwdStrategy = parcMemory_AllocateAndClear(sizeof(fwdStrategy));
+ assertNotNull(fwdStrategy, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(fwdStrategy));
+
+ fwdStrategy->prefix = prefix;
+ fwdStrategy->strategy = parcMemory_StringDuplicate(strategy, strlen(strategy));
+
+ return fwdStrategy;
+}
+
+char *
+cpiForwardingStrategy_ToString(CPIForwardingStrategy *fwdStrategy)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ char *ccnxName = ccnxName_ToString(cpiForwardingStrategy_GetPrefix(fwdStrategy));
+ parcBufferComposer_PutString(composer, ccnxName);
+ parcMemory_Deallocate((void **) &ccnxName);
+
+ parcBufferComposer_PutString(composer, cpiForwardingStrategy_GetStrategy(fwdStrategy));
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+
+ parcBufferComposer_Release(&composer);
+ return result;
+}
+
+
+CPIForwardingStrategy *
+cpiForwardingStrategy_Copy(const CPIForwardingStrategy *original)
+{
+ assertNotNull(original, "Parameter a must be non-null");
+ CPIForwardingStrategy *copy = cpiForwardingStrategy_Create(ccnxName_Copy(original->prefix),
+ parcMemory_StringDuplicate(original->strategy, strlen(original->strategy)));
+
+ return copy;
+}
+
+
+
+bool
+cpiForwardingStrategy_Equals(const CPIForwardingStrategy *a, const CPIForwardingStrategy *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+ if (a == b) {
+ return true;
+ }
+
+ if (ccnxName_Equals(a->prefix, b->prefix) && (strcmp(a->strategy, b->strategy) == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+const CCNxName *
+cpiForwardingStrategy_GetPrefix(const CPIForwardingStrategy *fwdStrategy)
+{
+ assertNotNull(fwdStrategy, "Parameter must be non-null");
+ return fwdStrategy->prefix;
+}
+
+const char *
+cpiForwardingStrategy_GetStrategy(const CPIForwardingStrategy *fwdStrategy)
+{
+ assertNotNull(fwdStrategy, "Parameter must be non-null");
+ return fwdStrategy->strategy;
+}
+
+PARCJSON *
+cpiForwardingStrategy_ToJson(const CPIForwardingStrategy *fwdStrategy)
+{
+ assertNotNull(fwdStrategy, "Parameter must be non-null");
+
+ PARCJSON *fwdStrategyJson = parcJSON_Create();
+ char *uri = ccnxName_ToString(fwdStrategy->prefix);
+ parcJSON_AddString(fwdStrategyJson, cpiPrefix, uri);
+ parcMemory_Deallocate((void **) &uri);
+
+ parcJSON_AddString(fwdStrategyJson, cpiStrategy, fwdStrategy->strategy);
+
+ return fwdStrategyJson;
+}
+
+CPIForwardingStrategy *
+cpiForwardingStrategy_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter json must be non-null");
+ PARCJSON *fwdStrategyJson = json;
+
+ PARCJSONValue *value = parcJSON_GetValueByName(fwdStrategyJson, cpiPrefix);
+ assertNotNull(value, "Couldn't locate tag %s in: %s", cpiPrefix, parcJSON_ToString(json));
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ CCNxName *prefix = ccnxName_CreateFromCString(parcBuffer_Overlay(sBuf, 0));
+
+ value = parcJSON_GetValueByName(fwdStrategyJson, cpiStrategy);
+ assertNotNull(value, "Couldn't locate tag %s in: %s", cpiStrategy, parcJSON_ToString(json));
+ sBuf = parcJSONValue_GetString(value);
+ char *strategy = parcBuffer_Overlay(sBuf, 0);
+
+ CPIForwardingStrategy *fwdStrategy = cpiForwardingStrategy_Create(prefix, strategy);
+
+ return fwdStrategy;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.h b/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.h
new file mode 100644
index 00000000..b50dff96
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ForwardingStrategy.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.
+ */
+
+#ifndef libccnx_cpi_ForwardingStrategy_h
+#define libccnx_cpi_ForwardingStrategy_h
+
+#include <ccnx/common/ccnx_Name.h>
+#include <ccnx/api/control/cpi_Address.h>
+
+#include <parc/algol/parc_JSON.h>
+
+struct cpi_forwarding_strategy;
+/**
+ * @typedef CPIForwardingStrategy
+ * @brief A representation of a forwarding strategy.
+ */
+typedef struct cpi_forwarding_strategy CPIForwardingStrategy;
+
+/**
+ */
+
+
+void cpiForwardingStrategy_Destroy(CPIForwardingStrategy **fwdStrategyPtr);
+
+CPIForwardingStrategy *cpiForwardingStrategy_Create(CCNxName *prefix, char *strategy);
+
+char *cpiForwardingStrategy_ToString(CPIForwardingStrategy *fwdStrategy);
+
+CPIForwardingStrategy *cpiForwardingStrategy_Copy(const CPIForwardingStrategy *original);
+
+bool cpiForwardingStrategy_Equals(const CPIForwardingStrategy *a, const CPIForwardingStrategy *b);
+
+const CCNxName *cpiForwardingStrategy_GetPrefix(const CPIForwardingStrategy *fwdStrategy);
+
+const char *cpiForwardingStrategy_GetStrategy(const CPIForwardingStrategy *fwdStrategy);
+
+PARCJSON *cpiForwardingStrategy_ToJson(const CPIForwardingStrategy *fwdStrategy);
+
+CPIForwardingStrategy *cpiForwardingStrategy_FromJson(PARCJSON *json);
+
+#endif // libccnx_cpi_ForwardingStrategy_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Interface.c b/libccnx-transport-rta/ccnx/api/control/cpi_Interface.c
new file mode 100644
index 00000000..320ca11d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Interface.c
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/cpi_AddressList.h>
+#include <ccnx/api/control/cpi_Interface.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_BufferComposer.h>
+#include <parc/algol/parc_Object.h>
+
+#include <LongBow/runtime.h>
+
+struct cpi_interface {
+ char *name;
+ unsigned interfaceIndex;
+ bool loopback;
+ bool supportMulticast;
+ unsigned mtu;
+
+ CPIAddressList *addressList;
+};
+
+char *
+cpiInterface_ToString(const CPIInterface *interface)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ parcBufferComposer_Format(composer, "%3u %10s %1s%1s %8u ",
+ interface->interfaceIndex,
+ interface->name,
+ interface->loopback ? "l" : " ",
+ interface->supportMulticast ? "m" : " ",
+ interface->mtu);
+
+ for (size_t i = 0; i < cpiAddressList_Length(interface->addressList); i++) {
+ cpiAddress_BuildString(cpiAddressList_GetItem(interface->addressList, i), composer);
+ if (i < (cpiAddressList_Length(interface->addressList) - 1)) {
+ parcBufferComposer_PutStrings(composer, "\n", NULL);
+ }
+ }
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+ return result;
+}
+
+CPIInterface *
+cpiInterface_Create(const char *name, unsigned interfaceIndex, bool loopback, bool supportMulticast, unsigned mtu)
+{
+ CPIInterface *iface = parcMemory_AllocateAndClear(sizeof(CPIInterface));
+
+ assertNotNull(iface, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterface));
+ iface->name = parcMemory_StringDuplicate(name, 64);
+ iface->interfaceIndex = interfaceIndex;
+ iface->loopback = loopback;
+ iface->supportMulticast = supportMulticast;
+ iface->mtu = mtu;
+ iface->addressList = cpiAddressList_Create();
+
+ return iface;
+}
+
+void
+cpiInterface_Destroy(CPIInterface **interfacePtr)
+{
+ assertNotNull(interfacePtr, "Parameter must be non-null double pointer");
+ assertNotNull(*interfacePtr, "Parameter must dereference to non-null pointer");
+
+ CPIInterface *iface = *interfacePtr;
+ parcMemory_Deallocate((void **) &iface->name);
+ cpiAddressList_Destroy(&iface->addressList);
+ parcMemory_Deallocate((void **) &iface);
+ interfacePtr = NULL;
+}
+
+void
+cpiInterface_AddAddress(CPIInterface *iface, CPIAddress *address)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+
+ size_t length = cpiAddressList_Length(iface->addressList);
+ for (size_t i = 0; i < length; i++) {
+ const CPIAddress *a = cpiAddressList_GetItem(iface->addressList, i);
+ if (cpiAddress_Equals(a, address)) {
+ return;
+ }
+ }
+
+ cpiAddressList_Append(iface->addressList, address);
+}
+
+const CPIAddressList *
+cpiInterface_GetAddresses(const CPIInterface *iface)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+ return iface->addressList;
+}
+
+unsigned
+cpiInterface_GetInterfaceIndex(const CPIInterface *iface)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+ return iface->interfaceIndex;
+}
+
+bool
+cpiInterface_NameEquals(const CPIInterface *iface, const char *name)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+
+ if (strcasecmp(iface->name, name) == 0) {
+ return true;
+ }
+ return false;
+}
+
+bool
+cpiInterface_Equals(const CPIInterface *a, const CPIInterface *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (a->interfaceIndex == b->interfaceIndex) {
+ if (a->loopback == b->loopback) {
+ if (a->supportMulticast == b->supportMulticast) {
+ if (a->mtu == b->mtu) {
+ if (strcasecmp(a->name, b->name) == 0) {
+ if (cpiAddressList_Equals(a->addressList, b->addressList)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+static const char cpi_Iface[] = "Interface";
+static const char cpi_IfName[] = "Name";
+static const char cpi_IFIDX[] = "Index";
+static const char cpi_IsLoopback[] = "Loopback";
+static const char cpi_Multicast[] = "Multicast";
+static const char cpi_MTU[] = "MTU";
+
+static const char cpi_True[] = "true";
+static const char cpi_False[] = "false";
+static const char cpi_Addrs[] = "Addrs";
+
+PARCJSON *
+cpiInterface_ToJson(CPIInterface *iface)
+{
+ assertNotNull(iface, "Parameter must be non-null");
+
+ PARCJSON *inner_json = parcJSON_Create();
+
+ parcJSON_AddString(inner_json, cpi_IfName, iface->name);
+ parcJSON_AddInteger(inner_json, cpi_IFIDX, iface->interfaceIndex);
+ parcJSON_AddString(inner_json, cpi_IsLoopback, iface->loopback ? cpi_True : cpi_False);
+ parcJSON_AddString(inner_json, cpi_Multicast, iface->supportMulticast ? cpi_True : cpi_False);
+ parcJSON_AddInteger(inner_json, cpi_MTU, iface->mtu);
+
+ PARCJSONArray *addrsArray = cpiAddressList_ToJson(iface->addressList);
+ parcJSON_AddArray(inner_json, cpi_Addrs, addrsArray);
+ parcJSONArray_Release(&addrsArray);
+
+ PARCJSON *outter_json = parcJSON_Create();
+ parcJSON_AddObject(outter_json, cpi_Iface, inner_json);
+ parcJSON_Release(&inner_json);
+
+ return outter_json;
+}
+
+CPIInterface *
+cpiInterface_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpi_Iface);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpi_Iface,
+ parcJSON_ToString(json));
+ PARCJSON *ifaceJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(ifaceJson, cpi_IfName);
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ const char *name = parcBuffer_Overlay(sBuf, 0);
+ value = parcJSON_GetValueByName(ifaceJson, cpi_IFIDX);
+ unsigned ifidx = (unsigned) parcJSONValue_GetInteger(value);
+ value = parcJSON_GetValueByName(ifaceJson, cpi_IsLoopback);
+ sBuf = parcJSONValue_GetString(value);
+ const char *loopStr = parcBuffer_Overlay(sBuf, 0);
+ value = parcJSON_GetValueByName(ifaceJson, cpi_Multicast);
+ sBuf = parcJSONValue_GetString(value);
+ const char *mcastStr = parcBuffer_Overlay(sBuf, 0);
+ value = parcJSON_GetValueByName(ifaceJson, cpi_MTU);
+ unsigned mtu = (unsigned) parcJSONValue_GetInteger(value);
+ value = parcJSON_GetValueByName(ifaceJson, cpi_Addrs);
+ PARCJSONArray *addrsJson = parcJSONValue_GetArray(value);
+
+ bool isLoopback = (strcasecmp(loopStr, cpi_True) == 0);
+ bool supportsMulticast = (strcasecmp(mcastStr, cpi_True) == 0);
+
+ CPIInterface *iface = cpiInterface_Create(name, ifidx, isLoopback, supportsMulticast, mtu);
+
+ CPIAddressList *addrs = cpiAddressList_CreateFromJson(addrsJson);
+ for (size_t i = 0; i < cpiAddressList_Length(addrs); i++) {
+ const CPIAddress *addr = cpiAddressList_GetItem(addrs, i);
+ cpiInterface_AddAddress(iface, cpiAddress_Copy(addr));
+ }
+
+ cpiAddressList_Destroy(&addrs);
+ return iface;
+}
+
+const char *
+cpiInterface_GetName(const CPIInterface *iface)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+ return iface->name;
+}
+
+unsigned
+cpiInterface_GetMTU(const CPIInterface *iface)
+{
+ assertNotNull(iface, "Parameter iface must be non-null");
+ return iface->mtu;
+}
+
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Interface.h b/libccnx-transport-rta/ccnx/api/control/cpi_Interface.h
new file mode 100644
index 00000000..0ed250b9
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Interface.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Interface.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_Interface_h
+#define libccnx_cpi_Interface_h
+
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_AddressList.h>
+
+struct cpi_interface;
+typedef struct cpi_interface CPIInterface;
+
+/**
+ * Creates a representation of an interface
+ *
+ * The name is copied. Creates a representation of a system interface.
+ *
+ * @param <#param1#>
+ * @return An allocated object, you must call <code>cpiInterface_Destroy()</code>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *cpiInterface_Create(const char *name, unsigned interfaceIndex, bool loopback, bool supportMulticast, unsigned mtu);
+
+void cpiInterface_Destroy(CPIInterface **interfacePtr);
+
+/**
+ * Creates an Interface object based on a JSON description.
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated object, you must call <code>cpiInterface_Destroy()</code>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *cpiInterface_FromJson(PARCJSON *json);
+
+/**
+ * Creates a JSON description of the object
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return An allocated object, you must destroy it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiInterface_ToJson(CPIInterface *iface);
+
+/**
+ * Adds an address to an interface
+ *
+ * Does not allow duplicates, if already exists is not added again
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiInterface_AddAddress(CPIInterface *iface, CPIAddress *address);
+
+/**
+ * Retrieves a list of interface addresses
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Will not be NULL, but may be empty
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const CPIAddressList *cpiInterface_GetAddresses(const CPIInterface *iface);
+
+/**
+ * The interface index
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+unsigned cpiInterface_GetInterfaceIndex(const CPIInterface *iface);
+
+/**
+ * Returns the interface name, e.g. "eth0"
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] iface An allocated CPIInterface
+ *
+ * @return non-null The interface Name as a C-string
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiInterface_GetName(const CPIInterface *iface);
+
+/**
+ * Returns the Maximum Transmission Unit (MTU) of the interface
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] iface An allocated CPIInterface
+ *
+ * @return number The MTU as reported by the kernel
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+unsigned cpiInterface_GetMTU(const CPIInterface *iface);
+
+/**
+ * Determine if two CPIInterfaceName instances are equal.
+ *
+ *
+ * The following equivalence relations on non-null `CPIInterfaceName` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceName_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiInterfaceName_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceName_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceName_Equals(x, y)` returns true and
+ * `cpiInterfaceName_Equals(y, z)` returns true,
+ * then `cpiInterfaceName_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceName_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceName_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceName` instance.
+ * @param b A pointer to a `CPIInterfaceName` instance.
+ * @return true if the two `CPIInterfaceName` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceName *a = cpiInterfaceName_Create();
+ * CPIInterfaceName *b = cpiInterfaceName_Create();
+ *
+ * if (cpiInterfaceName_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterface_NameEquals(const CPIInterface *iface, const char *name);
+
+/**
+ * Two CPIInterfaces are idential
+ *
+ * All properties must be the same. The order of addresses matters, and
+ * they must have been added to the address list in the same order.
+ *
+ * The interface name match is case in-sensitive.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiInterface_Equals(const CPIInterface *a, const CPIInterface *b);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param interface A CPIInterface structure pointer.
+ * @return An allocate string representation of the CPIInterface that must be freed via parcMemory_Deallocate().
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+char *cpiInterface_ToString(const CPIInterface *interface);
+#endif // libccnx_cpi_Interface_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.c
new file mode 100644
index 00000000..e6fb9621
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.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 <config.h>
+#include <stdio.h>
+
+#include <ccnx/api/control/cpi_InterfaceEthernet.h>
+#include <ccnx/api/control/cpi_InterfaceGeneric.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+#include <string.h>
+
+const static char cpiIFIDX[] = "IFIDX";
+const static char cpiADDRS[] = "ADDRS";
+const static char cpiSTATE[] = "STATE";
+
+static const char *cpiAddEtherConnection = "AddConnEther";
+
+struct cpi_interface_ethernet {
+ CPIInterfaceGeneric *generic;
+};
+
+CPIInterfaceEthernet *
+cpiInterfaceEthernet_Create(unsigned ifidx, CPIAddressList *addresses)
+{
+ assertNotNull(addresses, "Parameter addresses must be non-null");
+
+ CPIInterfaceEthernet *ethernet = parcMemory_AllocateAndClear(sizeof(CPIInterfaceEthernet));
+ assertNotNull(ethernet, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceEthernet));
+ ethernet->generic = cpiInterfaceGeneric_Create(ifidx, addresses);
+ return ethernet;
+}
+
+CPIInterfaceEthernet *
+cpiInterfaceEthernet_Copy(const CPIInterfaceEthernet *original)
+{
+ assertNotNull(original, "Parameter original must be non-null");
+
+ CPIInterfaceEthernet *ethernet = parcMemory_AllocateAndClear(sizeof(CPIInterfaceEthernet));
+ assertNotNull(ethernet, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceEthernet));
+ ethernet->generic = cpiInterfaceGeneric_Copy(original->generic);
+ return ethernet;
+}
+
+void
+cpiInterfaceEthernet_Destroy(CPIInterfaceEthernet **ethernetPtr)
+{
+ assertNotNull(ethernetPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*ethernetPtr, "Parameter must dereference to non-null pointer");
+
+ CPIInterfaceEthernet *ethernet = *ethernetPtr;
+ cpiInterfaceGeneric_Destroy(&ethernet->generic);
+ parcMemory_Deallocate((void **) &ethernet);
+ *ethernetPtr = NULL;
+}
+
+void
+cpiInterfaceEthernet_SetState(CPIInterfaceEthernet *ethernet, CPIInterfaceStateType state)
+{
+ assertNotNull(ethernet, "Parameter must be non-null pointer");
+ cpiInterfaceGeneric_SetState(ethernet->generic, state);
+}
+
+unsigned
+cpiInterfaceEthernet_GetIndex(const CPIInterfaceEthernet *ethernet)
+{
+ assertNotNull(ethernet, "Parameter must be non-null pointer");
+ return cpiInterfaceGeneric_GetIndex(ethernet->generic);
+}
+
+const CPIAddressList *
+cpiInterfaceEthernet_GetAddresses(const CPIInterfaceEthernet *ethernet)
+{
+ assertNotNull(ethernet, "Parameter must be non-null pointer");
+ return cpiInterfaceGeneric_GetAddresses(ethernet->generic);
+}
+
+CPIInterfaceStateType
+cpiInterfaceEthernet_GetState(const CPIInterfaceEthernet *ethernet)
+{
+ assertNotNull(ethernet, "Parameter must be non-null pointer");
+ return cpiInterfaceGeneric_GetState(ethernet->generic);
+}
+
+PARCJSON *
+cpiInterfaceEthernet_ToJson(const CPIInterfaceEthernet *ethernet)
+{
+ assertNotNull(ethernet, "Parameter must be non-null");
+
+ PARCJSON *innerJson = parcJSON_Create();
+
+ parcJSON_AddInteger(innerJson, cpiIFIDX, cpiInterfaceEthernet_GetIndex(ethernet));
+
+ if (cpiInterfaceEthernet_GetState(ethernet) != CPI_IFACE_UNKNOWN) {
+ parcJSON_AddString(innerJson,
+ cpiSTATE,
+ cpiInterfaceStateType_ToString(cpiInterfaceEthernet_GetState(ethernet)));
+ }
+
+ PARCJSONArray *addrsArray = cpiAddressList_ToJson(cpiInterfaceEthernet_GetAddresses(ethernet));
+ parcJSON_AddArray(innerJson, cpiADDRS, addrsArray);
+ parcJSONArray_Release(&addrsArray);
+
+ PARCJSON *result = parcJSON_Create();
+ parcJSON_AddObject(result, cpiInterfaceType_ToString(CPI_IFACE_ETHERNET), innerJson);
+ parcJSON_Release(&innerJson);
+
+ return result;
+}
+
+CPIInterfaceEthernet *
+cpiInterfaceEthernet_CreateFromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiInterfaceType_ToString(CPI_IFACE_ETHERNET));
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiInterfaceType_ToString(CPI_IFACE_ETHERNET),
+ parcJSON_ToString(json));
+ PARCJSON *etherJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(etherJson, cpiIFIDX);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiIFIDX,
+ parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsNumber(value),
+ "%s is not a number: %s",
+ cpiIFIDX,
+ parcJSON_ToString(json));
+ unsigned ifidx = (unsigned) parcJSONValue_GetInteger(value);
+
+ value = parcJSON_GetValueByName(etherJson, cpiADDRS);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiADDRS,
+ parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsArray(value),
+ "%s is not a number: %s",
+ cpiADDRS,
+ parcJSON_ToString(json));
+ PARCJSONArray *addrsJson = parcJSONValue_GetArray(value);
+
+ CPIAddressList *addrs = cpiAddressList_CreateFromJson(addrsJson);
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(ifidx, addrs);
+
+ value = parcJSON_GetValueByName(etherJson, cpiSTATE);
+ if (value != NULL) {
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ char *state = parcBuffer_Overlay(sBuf, 0);
+ cpiInterfaceEthernet_SetState(ethernet, cpiInterfaceStateType_FromString(state));
+ }
+
+ return ethernet;
+}
+
+bool
+cpiInterfaceEthernet_Equals(const CPIInterfaceEthernet *a, const CPIInterfaceEthernet *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ return cpiInterfaceGeneric_Equals(a->generic, b->generic);
+}
+
+const char *
+cpiLinks_AddEtherConnectionJasonTag()
+{
+ return cpiAddEtherConnection;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.h
new file mode 100644
index 00000000..11164891
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceEthernet.h
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceEthernet.h
+ * @brief Specialization of InterfaceGeneric to Ethernet
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceEthernet_h
+#define libccnx_cpi_InterfaceEthernet_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_AddressList.h>
+
+
+struct cpi_interface_ethernet;
+typedef struct cpi_interface_ethernet CPIInterfaceEthernet;
+
+/**
+ * Creates an Ethernet-like interface abstraction. Takes ownership of addresses.
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceEthernet *cpiInterfaceEthernet_Create(unsigned ifidx, CPIAddressList *addresses);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceEthernet *cpiInterfaceEthernet_Copy(const CPIInterfaceEthernet *ethernet);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceEthernet_Destroy(CPIInterfaceEthernet **ethernetPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceEthernet_SetState(CPIInterfaceEthernet *ethernet, CPIInterfaceStateType state);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceEthernet_GetIndex(const CPIInterfaceEthernet *ethernet);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddressList *cpiInterfaceEthernet_GetAddresses(const CPIInterfaceEthernet *ethernet);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceEthernet_GetState(const CPIInterfaceEthernet *ethernet);
+
+/**
+ * Determine if two CPIInterfaceEthernet instances are equal.
+ *
+ * Two CPIInterfaceEthernet instances are equal if, and only if, the same state and index and addresses
+ * are equal in the same order.
+ *
+ * The following equivalence relations on non-null `CPIInterfaceEthernet` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceEthernet_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiInterfaceEthernet_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceEthernet_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceEthernet_Equals(x, y)` returns true and
+ * `cpiInterfaceEthernet_Equals(y, z)` returns true,
+ * then `cpiInterfaceEthernet_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceEthernet_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceEthernet_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceEthernet` instance.
+ * @param b A pointer to a `CPIInterfaceEthernet` instance.
+ * @return true if the two `CPIInterfaceEthernet` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceEthernet *a = cpiInterfaceEthernet_Create();
+ * CPIInterfaceEthernet *b = cpiInterfaceEthernet_Create();
+ *
+ * if (cpiInterfaceEthernet_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterfaceEthernet_Equals(const CPIInterfaceEthernet *a, const CPIInterfaceEthernet *b);
+
+/**
+ * JSON representation
+ *
+ * <code>
+ * { "ETHERNET" :
+ * { "IFIDX" : ifidx,
+ * ["STATE" : "UP" | "DOWN", ]
+ * "ADDRS" : [ CPIAddress encodings ]
+ * }
+ * }
+ * </code>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiInterfaceEthernet_ToJson(const CPIInterfaceEthernet *ethernet);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceEthernet *cpiInterfaceEthernet_CreateFromJson(PARCJSON *json);
+#endif // libccnx_cpi_InterfaceEthernet_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.c
new file mode 100644
index 00000000..1914dcef
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.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 <ccnx/api/control/cpi_InterfaceGeneric.h>
+#include <LongBow/runtime.h>
+#include <parc/algol/parc_Memory.h>
+#include <string.h>
+
+struct cpi_interface_generic {
+ unsigned ifidx;
+ CPIInterfaceStateType state;
+ CPIAddressList *addresses;
+};
+
+CPIInterfaceGeneric *
+cpiInterfaceGeneric_Create(unsigned ifidx, CPIAddressList *addresses)
+{
+ assertNotNull(addresses, "Parameter addresses must be non-null");
+
+ CPIInterfaceGeneric *generic = parcMemory_AllocateAndClear(sizeof(CPIInterfaceGeneric));
+ assertNotNull(generic, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceGeneric));
+ generic->ifidx = ifidx;
+ generic->state = CPI_IFACE_UNKNOWN;
+ generic->addresses = addresses;
+ return generic;
+}
+
+CPIInterfaceGeneric *
+cpiInterfaceGeneric_Copy(const CPIInterfaceGeneric *original)
+{
+ assertNotNull(original, "Parameter original must be non-null");
+
+ CPIInterfaceGeneric *generic = parcMemory_AllocateAndClear(sizeof(CPIInterfaceGeneric));
+ assertNotNull(generic, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceGeneric));
+ generic->ifidx = original->ifidx;
+ generic->state = original->state;
+ generic->addresses = cpiAddressList_Copy(original->addresses);
+ return generic;
+}
+
+void
+cpiInterfaceGeneric_Destroy(CPIInterfaceGeneric **genericPtr)
+{
+ assertNotNull(genericPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*genericPtr, "Parameter must dereference to non-null pointer");
+
+ CPIInterfaceGeneric *generic = *genericPtr;
+ cpiAddressList_Destroy(&generic->addresses);
+ parcMemory_Deallocate((void **) &generic);
+ *genericPtr = NULL;
+}
+
+void
+cpiInterfaceGeneric_SetState(CPIInterfaceGeneric *generic, CPIInterfaceStateType state)
+{
+ assertNotNull(generic, "Parameter must be non-null pointer");
+ generic->state = state;
+}
+
+unsigned
+cpiInterfaceGeneric_GetIndex(const CPIInterfaceGeneric *generic)
+{
+ assertNotNull(generic, "Parameter must be non-null pointer");
+ return generic->ifidx;
+}
+
+const CPIAddressList *
+cpiInterfaceGeneric_GetAddresses(const CPIInterfaceGeneric *generic)
+{
+ assertNotNull(generic, "Parameter must be non-null pointer");
+ return generic->addresses;
+}
+
+CPIInterfaceStateType
+cpiInterfaceGeneric_GetState(const CPIInterfaceGeneric *generic)
+{
+ assertNotNull(generic, "Parameter must be non-null pointer");
+ return generic->state;
+}
+
+bool
+cpiInterfaceGeneric_Equals(const CPIInterfaceGeneric *a, const CPIInterfaceGeneric *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a == b) {
+ return true;
+ }
+
+ if (a->ifidx == b->ifidx) {
+ if (a->state == b->state) {
+ if (cpiAddressList_Equals(a->addresses, b->addresses)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+PARCBufferComposer *
+cpiInterfaceGeneric_BuildString(const CPIInterfaceGeneric *interface, PARCBufferComposer *composer)
+{
+ char *addressString = cpiAddressList_ToString(interface->addresses);
+ parcBufferComposer_Format(composer, "%5d %4s %s",
+ interface->ifidx,
+ cpiInterfaceStateType_ToString(interface->state),
+ addressString
+ );
+ parcMemory_Deallocate((void **) &addressString);
+ return composer;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.h
new file mode 100644
index 00000000..ffdd0978
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceGeneric.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 cpi_InterfaceGeneric.h
+ * @brief A generic interface that is used as a super type for other interfaces.
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceGeneric_h
+#define libccnx_cpi_InterfaceGeneric_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_AddressList.h>
+
+
+struct cpi_interface_generic;
+typedef struct cpi_interface_generic CPIInterfaceGeneric;
+
+/**
+ * Creates an Generic-like interface abstraction. Takes ownership of addresses.
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceGeneric *cpiInterfaceGeneric_Create(unsigned ifidx, CPIAddressList *addresses);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceGeneric *cpiInterfaceGeneric_Copy(const CPIInterfaceGeneric *generic);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceGeneric_Destroy(CPIInterfaceGeneric **genericPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceGeneric_SetState(CPIInterfaceGeneric *generic, CPIInterfaceStateType state);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceGeneric_GetIndex(const CPIInterfaceGeneric *generic);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddressList *cpiInterfaceGeneric_GetAddresses(const CPIInterfaceGeneric *generic);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceGeneric_GetState(const CPIInterfaceGeneric *generic);
+
+/**
+ * Determine if two CPIInterfaceGeneric instances are equal.
+ *
+ * Two CPIInterfaceGeneric instances are equal if, and only if, the same state and index and addresses
+ * are equal in the same order.
+ *
+ * The following equivalence relations on non-null `CPIInterfaceGeneric` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceGeneric_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiInterfaceGeneric_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceGeneric_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceGeneric_Equals(x, y)` returns true and
+ * `cpiInterfaceGeneric_Equals(y, z)` returns true,
+ * then `cpiInterfaceGeneric_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceGeneric_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceGeneric_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceGeneric` instance.
+ * @param b A pointer to a `CPIInterfaceGeneric` instance.
+ * @return true if the two `CPIInterfaceGeneric` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceGeneric *a = cpiInterfaceGeneric_Create();
+ * CPIInterfaceGeneric *b = cpiInterfaceGeneric_Create();
+ *
+ * if (cpiInterfaceGeneric_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterfaceGeneric_Equals(const CPIInterfaceGeneric *a, const CPIInterfaceGeneric *b);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param [in] interface
+ * @param [in] composer A pointer to a PARCBufferComposer instance.
+ * @return return The input parameter string.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCBufferComposer *cpiInterfaceGeneric_BuildString(const CPIInterfaceGeneric *interface, PARCBufferComposer *composer);
+#endif // libccnx_cpi_InterfaceGeneric_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPMulticast.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPMulticast.h
new file mode 100644
index 00000000..b218a407
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPMulticast.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceIPMulticast.h
+ * @brief UDP Multicast overlay
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceIPMulticast_h
+#define libccnx_cpi_InterfaceIPMulticast_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+
+struct cpi_interface_ipmcast;
+/**
+ *
+ * @see cpiInterfaceIPMulticast_Create
+ */
+typedef struct cpi_interface_ipmcast CPIInterfaceIPMulticast;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPMulticast *cpiInterfaceIPMulticast_Create(unsigned ifidx, CPIInterfaceStateType state, CPIAddress *source, CPIAddress *group, IPTunnelType tunnelType);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPMulticast *cpiInterfaceIPMulticast_Copy(const CPIInterfaceIPMulticast *ipmcast);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceIPMulticast_Destroy(CPIInterfaceIPMulticast **ipmcastPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceIPMulticast_GetIndex(const CPIInterfaceIPMulticast *ipmcast);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceIPMulticast_GetSourceAddress(const CPIInterfaceIPMulticast *ipmcast);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceIPMulticast_GetGroupAddress(const CPIInterfaceIPMulticast *ipmcast);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+IPTunnelType cpiInterfaceIPMulticast_GetTunnelType(const CPIInterfaceIPMulticast *ipmcast);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceIPMulticast_GetState(const CPIInterfaceIPMulticast *ipmcast);
+#endif // libccnx_cpi_InterfaceIPMulticast_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.c
new file mode 100644
index 00000000..92f77a29
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <parc/algol/parc_Object.h>
+#include <parc/algol/parc_JSON.h>
+
+#include <ccnx/api/control/cpi_InterfaceIPTunnel.h>
+#include <ccnx/api/control/cpi_InterfaceGeneric.h>
+
+#define SOURCE_INDEX 0
+#define DESTINATION_INDEX 1
+
+const static char cpiIFIDX[] = "IFIDX";
+const static char cpiSRCADDR[] = "SRC";
+const static char cpiDSTADDR[] = "DST";
+const static char cpiTUNTYPE[] = "TUNTYPE";
+const static char cpiSTATE[] = "STATE";
+const static char cpiSYMBOLIC[] = "SYMBOLIC";
+
+struct cpi_interface_iptun {
+ CPIInterfaceGeneric *generic;
+ CPIInterfaceIPTunnelType tunnelType;
+ char *symbolic;
+};
+
+struct iptunnel_type_string_s {
+ CPIInterfaceIPTunnelType type;
+ const char *str;
+} iptunnelTypeStrings[] = {
+ { .type = IPTUN_UDP, .str = "UDP" },
+ { .type = IPTUN_TCP, .str = "TCP" },
+ { .type = IPTUN_GRE, .str = "GRE" },
+ { .type = 0, .str = NULL },
+};
+
+
+static void
+_cpiInterfaceIPTunnel_Destroy(CPIInterfaceIPTunnel **iptunPtr)
+{
+ assertNotNull(iptunPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*iptunPtr, "Parameter must dereference to non-null pointer");
+
+ CPIInterfaceIPTunnel *iptun = *iptunPtr;
+ cpiInterfaceGeneric_Destroy(&iptun->generic);
+ parcMemory_Deallocate((void **) &iptun->symbolic);
+}
+
+parcObject_ExtendPARCObject(CPIInterfaceIPTunnel, _cpiInterfaceIPTunnel_Destroy, cpiInterfaceIPTunnel_Copy, NULL, cpiInterfaceIPTunnel_Equals, NULL, NULL, cpiInterfaceIPTunnel_ToJson);
+
+parcObject_ImplementRelease(cpiInterfaceIPTunnel, CPIInterfaceIPTunnel);
+
+parcObject_ImplementAcquire(cpiInterfaceIPTunnel, CPIInterfaceIPTunnel);
+
+const char *
+cpiInterfaceIPTunnel_TypeToString(CPIInterfaceIPTunnelType type)
+{
+ for (int i = 0; iptunnelTypeStrings[i].str != NULL; i++) {
+ if (iptunnelTypeStrings[i].type == type) {
+ return iptunnelTypeStrings[i].str;
+ }
+ }
+ assertTrue(0, "Unknown type: %d", type);
+ abort();
+}
+
+CPIInterfaceIPTunnelType
+cpiInterfaceIPTunnel_TypeFromString(const char *str)
+{
+ for (int i = 0; iptunnelTypeStrings[i].str != NULL; i++) {
+ if (strcasecmp(iptunnelTypeStrings[i].str, str) == 0) {
+ return iptunnelTypeStrings[i].type;
+ }
+ }
+ assertTrue(0, "Unknown stirng: %s", str);
+ abort();
+}
+
+CPIInterfaceIPTunnel *
+cpiInterfaceIPTunnel_Create(unsigned ifidx, CPIAddress *source, CPIAddress *destination, CPIInterfaceIPTunnelType tunnelType, const char *symbolic)
+{
+ assertNotNull(source, "Parameter source must be non-null");
+ assertNotNull(destination, "Parameter destination must be non-null");
+
+ assertTrue(cpiAddress_GetType(source) == cpiAddressType_INET || cpiAddress_GetType(source) == cpiAddressType_INET6,
+ "source address unsupported type: %d",
+ cpiAddress_GetType(source));
+
+ assertTrue(cpiAddress_GetType(destination) == cpiAddressType_INET || cpiAddress_GetType(destination) == cpiAddressType_INET6,
+ "destination address unsupported type: %d",
+ cpiAddress_GetType(destination));
+
+ CPIInterfaceIPTunnel *iptun = parcObject_CreateInstance(CPIInterfaceIPTunnel);
+ assertNotNull(iptun, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceIPTunnel));
+
+ CPIAddressList *addrlist = cpiAddressList_Create();
+ cpiAddressList_Append(addrlist, source);
+ cpiAddressList_Append(addrlist, destination);
+
+ iptun->generic = cpiInterfaceGeneric_Create(ifidx, addrlist);
+ iptun->tunnelType = tunnelType;
+ iptun->symbolic = parcMemory_StringDuplicate(symbolic, strlen(symbolic));
+
+ return iptun;
+}
+
+CPIInterfaceIPTunnel *
+cpiInterfaceIPTunnel_Copy(const CPIInterfaceIPTunnel *original)
+{
+ assertNotNull(original, "Parameter original must be non-null");
+ CPIInterfaceIPTunnel *iptun = parcObject_CreateInstance(CPIInterfaceIPTunnel);
+ assertNotNull(iptun, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceIPTunnel));
+ iptun->generic = cpiInterfaceGeneric_Copy(original->generic);
+ iptun->tunnelType = original->tunnelType;
+ iptun->symbolic = parcMemory_StringDuplicate(original->symbolic, strlen(original->symbolic));
+ return iptun;
+}
+
+void
+cpiInterfaceIPTunnel_SetState(CPIInterfaceIPTunnel *iptun, CPIInterfaceStateType state)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ cpiInterfaceGeneric_SetState(iptun->generic, state);
+}
+
+const char *
+cpiInterfaceIPTunnel_GetSymbolicName(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return iptun->symbolic;
+}
+
+unsigned
+cpiInterfaceIPTunnel_GetIndex(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return cpiInterfaceGeneric_GetIndex(iptun->generic);
+}
+
+const CPIAddress *
+cpiInterfaceIPTunnel_GetSourceAddress(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ const CPIAddressList *addrs = cpiInterfaceGeneric_GetAddresses(iptun->generic);
+ return cpiAddressList_GetItem(addrs, SOURCE_INDEX);
+}
+
+const CPIAddress *
+cpiInterfaceIPTunnel_GetDestinationAddress(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ const CPIAddressList *addrs = cpiInterfaceGeneric_GetAddresses(iptun->generic);
+ return cpiAddressList_GetItem(addrs, DESTINATION_INDEX);
+}
+
+CPIInterfaceIPTunnelType
+cpiInterfaceIPTunnel_GetTunnelType(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return iptun->tunnelType;
+}
+
+CPIInterfaceStateType
+cpiInterfaceIPTunnel_GetState(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+ return cpiInterfaceGeneric_GetState(iptun->generic);
+}
+
+bool
+cpiInterfaceIPTunnel_Equals(const CPIInterfaceIPTunnel *a, const CPIInterfaceIPTunnel *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+
+ if (a->tunnelType == b->tunnelType) {
+ if (cpiInterfaceGeneric_Equals(a->generic, b->generic)) {
+ if (strcasecmp(a->symbolic, b->symbolic) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * JSON representation
+ *
+ * <code>
+ * { "TUNNEL" :
+ * { "IFIDX" : ifidx,
+ * "SYMBOLIC" : "tun3",
+ * ["STATE" : "UP" | "DOWN", ]
+ * "TYPE": "UDP" | "TCP" | "GRE",
+ * "SRC" : {srcaddr},
+ * "DST" : {dstaddr}
+ * }
+ * }
+ * </code>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *
+cpiInterfaceIPTunnel_ToJson(const CPIInterfaceIPTunnel *iptun)
+{
+ assertNotNull(iptun, "Parameter must be non-null");
+
+ PARCJSON *inner_json = parcJSON_Create();
+
+ parcJSON_AddInteger(inner_json, cpiIFIDX, cpiInterfaceIPTunnel_GetIndex(iptun));
+ parcJSON_AddString(inner_json, cpiSYMBOLIC, iptun->symbolic);
+
+ if (cpiInterfaceIPTunnel_GetState(iptun) != CPI_IFACE_UNKNOWN) {
+ parcJSON_AddString(inner_json, cpiSTATE, cpiInterfaceStateType_ToString(cpiInterfaceIPTunnel_GetState(iptun)));
+ }
+ parcJSON_AddString(inner_json, cpiTUNTYPE, cpiInterfaceIPTunnel_TypeToString(cpiInterfaceIPTunnel_GetTunnelType(iptun)));
+
+ PARCJSON *json = cpiAddress_ToJson(cpiInterfaceIPTunnel_GetSourceAddress(iptun));
+ parcJSON_AddObject(inner_json, cpiSRCADDR, json);
+ parcJSON_Release(&json);
+
+ json = cpiAddress_ToJson(cpiInterfaceIPTunnel_GetDestinationAddress(iptun));
+ parcJSON_AddObject(inner_json, cpiDSTADDR, json);
+ parcJSON_Release(&json);
+
+ PARCJSON *outter_json = parcJSON_Create();
+ parcJSON_AddObject(outter_json, cpiInterfaceType_ToString(CPI_IFACE_TUNNEL), inner_json);
+ parcJSON_Release(&inner_json);
+
+ return outter_json;
+}
+
+CPIInterfaceIPTunnel *
+cpiInterfaceIPTunnel_CreateFromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiInterfaceType_ToString(CPI_IFACE_TUNNEL));
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiInterfaceType_ToString(CPI_IFACE_TUNNEL),
+ parcJSON_ToString(json));
+ PARCObject *tunnelJson = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(tunnelJson, cpiIFIDX);
+ assertNotNull(value, "Could not find key %s: %s", cpiIFIDX, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsNumber(value),
+ "%s is not a number: %s",
+ cpiIFIDX,
+ parcJSON_ToString(json));
+ PARCJSONValue *ifidx_value = value;
+
+ value = parcJSON_GetValueByName(tunnelJson, cpiSYMBOLIC);
+ assertNotNull(value, "Could not find key %s: %s", cpiSYMBOLIC, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsString(value),
+ "%s is not a string: %s",
+ cpiSYMBOLIC,
+ parcJSON_ToString(json));
+ PARCJSONValue *symbolic_value = value;
+
+ value = parcJSON_GetValueByName(tunnelJson, cpiTUNTYPE);
+ assertNotNull(value, "Could not find key %s: %s", cpiTUNTYPE, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsString(value),
+ "%s is not a number: %s",
+ cpiTUNTYPE,
+ parcJSON_ToString(json));
+ PARCJSONValue *tuntype_value = value;
+
+ value = parcJSON_GetValueByName(tunnelJson, cpiSRCADDR);
+ assertNotNull(value, "Could not find key %s: %s", cpiSRCADDR, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "%s is not an array: %s",
+ cpiSRCADDR,
+ parcJSON_ToString(json));
+ PARCJSONValue *srcaddr_value = value;
+
+ value = parcJSON_GetValueByName(tunnelJson, cpiDSTADDR);
+ assertNotNull(value, "Could not find key %s: %s", cpiDSTADDR, parcJSON_ToString(json));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "%s is not an array: %s",
+ cpiDSTADDR,
+ parcJSON_ToString(json));
+ PARCJSONValue *dstaddr_value = value;
+
+ unsigned ifidx = (unsigned) parcJSONValue_GetInteger(ifidx_value);
+ PARCBuffer *sBuf = parcJSONValue_GetString(symbolic_value);
+ const char *symbolic = parcBuffer_Overlay(sBuf, 0);
+ CPIAddress *srcaddr =
+ cpiAddress_CreateFromJson(parcJSONValue_GetJSON(srcaddr_value));
+ CPIAddress *dstaddr =
+ cpiAddress_CreateFromJson(parcJSONValue_GetJSON(dstaddr_value));
+ sBuf = parcJSONValue_GetString(tuntype_value);
+ CPIInterfaceIPTunnelType tunnelType =
+ cpiInterfaceIPTunnel_TypeFromString(parcBuffer_Overlay(sBuf, 0));
+
+ CPIInterfaceIPTunnel *iptun =
+ cpiInterfaceIPTunnel_Create(ifidx, srcaddr, dstaddr, tunnelType, symbolic);
+
+ PARCJSONValue *state_value = parcJSON_GetValueByName(tunnelJson, cpiSTATE);
+ if (state_value != NULL) {
+ sBuf = parcJSONValue_GetString(state_value);
+ cpiInterfaceIPTunnel_SetState(iptun, cpiInterfaceStateType_FromString(parcBuffer_Overlay(sBuf, 0)));
+ }
+
+ return iptun;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.h
new file mode 100644
index 00000000..e5a01a18
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnel.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceIPTunnel.h
+ * @brief Represents a point-to-point tunnel over IP.
+ *
+ * The carries can be UDP, TCP, or GRE
+ *
+ * We use InterfaceGeneric to back this type. We always use 2 addresses in the address list.
+ * Address 0 is the source and address 1 is the destination.
+ *
+ */
+#ifndef libccnx_cpi_InterfaceIPTunnel_h
+#define libccnx_cpi_InterfaceIPTunnel_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+
+#include <parc/algol/parc_JSON.h>
+
+struct cpi_interface_iptun;
+/**
+ *
+ * @see cpiInterfaceIPTunnel_Create
+ */
+typedef struct cpi_interface_iptun CPIInterfaceIPTunnel;
+
+typedef enum {
+ IPTUN_UDP,
+ IPTUN_TCP,
+ IPTUN_GRE
+} CPIInterfaceIPTunnelType;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] type A CPIInterfaceIPTunnelType value
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const char *cpiInterfaceIPTunnel_TypeToString(CPIInterfaceIPTunnelType type);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] str A nul-terminated C string.
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnelType cpiInterfaceIPTunnel_TypeFromString(const char *str);
+
+/**
+ * Creates a representation of an IP tunnel
+ *
+ * The symblic name will be used in the future to refer to this tunnel. It must be unique or the forwarder will reject the command.
+ *
+ * @param [in] ifidx The interface index of the tunnel (may be 0 if not known)
+ * @param [in] source The local address and optional port
+ * @param [in] destination The remote address and port
+ * @param [in] tunnelType The encapsulation protocol
+ * @param [in] symbolic The symbolic name to refer to this tunnel (e.g. 'tun2')
+ *
+ * @return non-null An allocated CPIInterfaceIPTunnel
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnel *cpiInterfaceIPTunnel_Create(unsigned ifidx, CPIAddress *source, CPIAddress *destination, CPIInterfaceIPTunnelType tunnelType, const char *symbolic);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnel *cpiInterfaceIPTunnel_Copy(const CPIInterfaceIPTunnel *ipTunnel);
+CPIInterfaceIPTunnel *cpiInterfaceIPTunnel_Acquire(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ * @param [in] state A CPIInterfaceStateType value.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceIPTunnel_SetState(CPIInterfaceIPTunnel *ipTunnel, CPIInterfaceStateType state);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnelPtr A pointer to a pointer to a valid CPIInterfaceIPTunnel instance.
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceIPTunnel_Release(CPIInterfaceIPTunnel **ipTunnelPtr);
+
+/**
+ * Returns the symbolic name of the tunnel
+ *
+ * The caller should make a copy of the string if it will be stored.
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return non-null The symbolic name
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const char *cpiInterfaceIPTunnel_GetSymbolicName(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceIPTunnel_GetIndex(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceIPTunnel_GetSourceAddress(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceIPTunnel_GetDestinationAddress(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnelType cpiInterfaceIPTunnel_GetTunnelType(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceIPTunnel_GetState(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * Determine if two CPIInterfaceIPTunnel instances are equal.
+ *
+ *
+ * The following equivalence relations on non-null `CPIInterfaceIPTunnel` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceIPTunnel_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiInterfaceIPTunnel_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceIPTunnel_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceIPTunnel_Equals(x, y)` returns true and
+ * `cpiInterfaceIPTunnel_Equals(y, z)` returns true,
+ * then `cpiInterfaceIPTunnel_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceIPTunnel_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceIPTunnel_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceIPTunnel` instance.
+ * @param b A pointer to a `CPIInterfaceIPTunnel` instance.
+ * @return true if the two `CPIInterfaceIPTunnel` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceIPTunnel *a = cpiInterfaceIPTunnel_Create();
+ * CPIInterfaceIPTunnel *b = cpiInterfaceIPTunnel_Create();
+ *
+ * if (cpiInterfaceIPTunnel_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterfaceIPTunnel_Equals(const CPIInterfaceIPTunnel *a, const CPIInterfaceIPTunnel *b);
+
+/**
+ * JSON representation
+ *
+ * <code>
+ * { "TUNNEL" :
+ * { "IFIDX" : ifidx,
+ * ["STATE" : "UP" | "DOWN", ]
+ * "TYPE": "UDP" | "TCP" | "GRE",
+ * "SRC" : {srcaddr},
+ * "DST" : {dstaddr}
+ * }
+ * }
+ * </code>
+ *
+ * @param [in] ipTunnel A pointer to a valid CPIInterfaceIPTunnel
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiInterfaceIPTunnel_ToJson(const CPIInterfaceIPTunnel *ipTunnel);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] json A pointer to a valid PARCJSON instance.
+ *
+ * @return non-NULL A pointer to a valid CPIInterfaceIPTunnel instance.
+ * @return NULL Memory could not be allocated.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnel *cpiInterfaceIPTunnel_CreateFromJson(PARCJSON *json);
+#endif // libccnx_cpi_InterfaceIPTunnel_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.c
new file mode 100644
index 00000000..7515258d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <parc/algol/parc_ArrayList.h>
+
+#include <ccnx/api/control/cpi_InterfaceIPTunnelList.h>
+#include <LongBow/runtime.h>
+
+struct cpi_interface_iptunnel_list {
+ PARCArrayList *listOfTunnels;
+};
+
+/**
+ * PARCArrayList entry destroyer
+ */
+static void
+_arrayDestroyer(void **voidPtr)
+{
+ CPIInterfaceIPTunnel **entryPtr = (CPIInterfaceIPTunnel **) voidPtr;
+ cpiInterfaceIPTunnel_Release(entryPtr);
+}
+
+CPIInterfaceIPTunnelList *
+cpiInterfaceIPTunnelList_Create(void)
+{
+ CPIInterfaceIPTunnelList *list = parcMemory_AllocateAndClear(sizeof(CPIInterfaceIPTunnelList));
+ assertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceIPTunnelList));
+ list->listOfTunnels = parcArrayList_Create(_arrayDestroyer);
+ return list;
+}
+
+void
+cpiInterfaceIPTunnelList_Destroy(CPIInterfaceIPTunnelList **listPtr)
+{
+ assertNotNull(listPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*listPtr, "Parameter must dereference to non-null pointer");
+ CPIInterfaceIPTunnelList *list = *listPtr;
+ parcArrayList_Destroy(&list->listOfTunnels);
+ parcMemory_Deallocate((void **) &list);
+ *listPtr = NULL;
+}
+
+void
+cpiInterfaceIPTunnelList_Append(CPIInterfaceIPTunnelList *list, CPIInterfaceIPTunnel *entry)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ assertNotNull(entry, "Parameter entry must be non-null");
+
+ parcArrayList_Add(list->listOfTunnels, (PARCObject *) entry);
+}
+
+size_t
+cpiInterfaceIPTunnelList_Length(const CPIInterfaceIPTunnelList *list)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ return parcArrayList_Size(list->listOfTunnels);
+}
+
+CPIInterfaceIPTunnel *
+cpiInterfaceIPTunnelList_Get(CPIInterfaceIPTunnelList *list, size_t index)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ CPIInterfaceIPTunnel *original = (CPIInterfaceIPTunnel *) parcArrayList_Get(list->listOfTunnels, index);
+ return cpiInterfaceIPTunnel_Copy(original);
+}
+
+bool
+cpiInterfaceIPTunnelList_Equals(const CPIInterfaceIPTunnelList *a, const CPIInterfaceIPTunnelList *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (parcArrayList_Size(a->listOfTunnels) == parcArrayList_Size(b->listOfTunnels)) {
+ size_t length = parcArrayList_Size(a->listOfTunnels);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterfaceIPTunnel *tunnel_a = (CPIInterfaceIPTunnel *) parcArrayList_Get(a->listOfTunnels, i);
+ CPIInterfaceIPTunnel *tunnel_b = (CPIInterfaceIPTunnel *) parcArrayList_Get(b->listOfTunnels, i);
+ if (!cpiInterfaceIPTunnel_Equals(tunnel_a, tunnel_b)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+const char cpi_InterfaceIPTunnelList[] = "TunnelList";
+
+PARCJSON *
+cpiInterfaceIPTunnelList_ToJson(const CPIInterfaceIPTunnelList *list)
+{
+ assertNotNull(list, "Parameter must be non-null");
+
+ PARCJSONArray *tunnelList = parcJSONArray_Create();
+
+ size_t length = parcArrayList_Size(list->listOfTunnels);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterfaceIPTunnel *tunnel = (CPIInterfaceIPTunnel *) parcArrayList_Get(list->listOfTunnels, i);
+ PARCJSON *json = cpiInterfaceIPTunnel_ToJson(tunnel);
+ PARCJSONValue *value = parcJSONValue_CreateFromJSON(json);
+ parcJSON_Release(&json);
+ parcJSONArray_AddValue(tunnelList, value);
+ parcJSONValue_Release(&value);
+ }
+
+ PARCJSON *result = parcJSON_Create();
+ parcJSON_AddArray(result, cpi_InterfaceIPTunnelList, tunnelList);
+ parcJSONArray_Release(&tunnelList);
+
+ return result;
+}
+
+CPIInterfaceIPTunnelList *
+cpiInterfaceIPTunnelList_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpi_InterfaceIPTunnelList);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpi_InterfaceIPTunnelList,
+ parcJSON_ToString(json));
+ PARCJSONArray *tunnelListJson = parcJSONValue_GetArray(value);
+
+ CPIInterfaceIPTunnelList *list = cpiInterfaceIPTunnelList_Create();
+
+ size_t length = parcJSONArray_GetLength(tunnelListJson);
+ for (size_t i = 0; i < length; i++) {
+ value = parcJSONArray_GetValue(tunnelListJson, i);
+ CPIInterfaceIPTunnel *tunnel =
+ cpiInterfaceIPTunnel_CreateFromJson(parcJSONValue_GetJSON(value));
+
+ cpiInterfaceIPTunnelList_Append(list, tunnel);
+ }
+
+ return list;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.h
new file mode 100644
index 00000000..e9ec80bf
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceIPTunnelList.h
@@ -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.
+ */
+
+/**
+ * @file cpi_InterfaceIPTunnelList.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceIPTunnelList_h
+#define libccnx_cpi_InterfaceIPTunnelList_h
+
+struct cpi_interface_iptunnel_list;
+typedef struct cpi_interface_iptunnel_list CPIInterfaceIPTunnelList;
+
+#include <ccnx/api/control/cpi_InterfaceIPTunnel.h>
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnelList *cpiInterfaceIPTunnelList_Create(void);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceIPTunnelList_Destroy(CPIInterfaceIPTunnelList **listPtr);
+
+/**
+ * Adds a iptunnel entry to the list.
+ *
+ * Appends <code>entry</code> to the list. Takes ownership of the entry
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiInterfaceIPTunnelList_Append(CPIInterfaceIPTunnelList *list, CPIInterfaceIPTunnel *entry);
+
+/**
+ * Determine if two CPIInterfaceIPTunnelList instances are equal.
+ *
+ * Two CPIInterfaceIPTunnelList instances are equal if, and only if,
+ * the size of the lists are equal and every element is equal and in the same order.
+ *
+ * The following equivalence relations on non-null `CPIInterfaceIPTunnelList` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceIPTunnelList_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiInterfaceIPTunnelList_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceIPTunnelList_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceIPTunnelList_Equals(x, y)` returns true and
+ * `cpiInterfaceIPTunnelList_Equals(y, z)` returns true,
+ * then `cpiInterfaceIPTunnelList_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceIPTunnelList_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceIPTunnelList_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceIPTunnelList` instance.
+ * @param b A pointer to a `CPIInterfaceIPTunnelList` instance.
+ * @return true if the two `CPIInterfaceIPTunnelList` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceIPTunnelList *a = cpiInterfaceIPTunnelList_Create();
+ * CPIInterfaceIPTunnelList *b = cpiInterfaceIPTunnelList_Create();
+ *
+ * if (cpiInterfaceIPTunnelList_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterfaceIPTunnelList_Equals(const CPIInterfaceIPTunnelList *a, const CPIInterfaceIPTunnelList *b);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+size_t cpiInterfaceIPTunnelList_Length(const CPIInterfaceIPTunnelList *list);
+
+/**
+ * Returns a reference counted copy of the iptunnel entry.
+ *
+ * Caller must destroy the returned value.
+ * Will assert if you go beyond the end of the list.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceIPTunnel *cpiInterfaceIPTunnelList_Get(CPIInterfaceIPTunnelList *list, size_t index);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiInterfaceIPTunnelList_ToJson(const CPIInterfaceIPTunnelList *list);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnelList *cpiInterfaceIPTunnelList_FromJson(PARCJSON *json);
+#endif // libccnx_cpi_InterfaceIPTunnelList_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceL2Group.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceL2Group.h
new file mode 100644
index 00000000..2104c430
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceL2Group.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceL2Group.h
+ * @brief Layer 2 group address overlay
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceL2Group_h
+#define libccnx_cpi_InterfaceL2Group_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+
+struct cpi_interface_l2group;
+/**
+ *
+ * @see cpiInterfaceL2Group_Create
+ */
+typedef struct cpi_interface_l2group CPIInterfaceL2Group;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceL2Group *cpiInterfaceL2Group_Create(unsigned ifidx, CPIInterfaceStateType state, CPIAddress *source, CPIAddress *group, IPTunnelType tunnelType);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceL2Group *cpiInterfaceL2Group_Copy(const CPIInterfaceL2Group *l2group);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceL2Group_Destroy(CPIInterfaceL2Group **l2groupPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceL2Group_GetIndex(const CPIInterfaceL2Group *l2group);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceL2Group_GetSourceAddress(const CPIInterfaceL2Group *l2group);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddress *cpiInterfaceL2Group_GetGroupAddress(const CPIInterfaceL2Group *l2group);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+IPTunnelType cpiInterfaceL2Group_GetTunnelType(const CPIInterfaceL2Group *l2group);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceL2Group_GetState(const CPIInterfaceL2Group *l2group);
+#endif // libccnx_cpi_InterfaceL2Group_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceLocal.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceLocal.h
new file mode 100644
index 00000000..601dd554
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceLocal.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceLocal.h
+ * @brief A local interface points up to the transport
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceLocal_h
+#define libccnx_cpi_InterfaceLocal_h
+
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_AddressList.h>
+
+struct cpi_interface_local;
+/**
+ * @see cpiInterfaceLocal_Create
+ */
+typedef struct cpi_interface_local CPIInterfaceLocal;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceLocal *cpiInterfaceLocal_Create(unsigned ifidx, CPIInterfaceStateType state, CPIAddressList *addresses);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceLocal *cpiInterfaceLocal_Copy(const CPIInterfaceLocal *local);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceLocal_Destroy(CPIInterfaceLocal **localPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+unsigned cpiInterfaceLocal_GetIndex(const CPIInterfaceLocal *local);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+const CPIAddressList *cpiInterfaceLocal_GetAddresses(const CPIInterfaceLocal *local);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceStateType cpiInterfaceLocal_GetState(const CPIInterfaceLocal *local);
+#endif // libccnx_cpi_InterfaceLocal_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.c
new file mode 100644
index 00000000..0a9042cf
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/cpi_InterfaceSet.h>
+
+#include <parc/algol/parc_ArrayList.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <LongBow/runtime.h>
+
+struct cpi_interface_set {
+ PARCArrayList *listOfInterfaces;
+};
+
+static void
+_destroyInterface(void **ifaceVoidPtr)
+{
+ cpiInterface_Destroy((CPIInterface **) ifaceVoidPtr);
+}
+
+CPIInterfaceSet *
+cpiInterfaceSet_Create(void)
+{
+ CPIInterfaceSet *set = parcMemory_AllocateAndClear(sizeof(CPIInterfaceSet));
+ assertNotNull(set, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIInterfaceSet));
+ set->listOfInterfaces = parcArrayList_Create(_destroyInterface);
+ return set;
+}
+
+void
+cpiInterfaceSet_Destroy(CPIInterfaceSet **setPtr)
+{
+ assertNotNull(setPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*setPtr, "Parameter must dereference to non-null pointer");
+
+ CPIInterfaceSet *set = *setPtr;
+ parcArrayList_Destroy(&set->listOfInterfaces);
+ parcMemory_Deallocate((void **) &set);
+ *setPtr = NULL;
+}
+
+bool
+cpiInterfaceSet_Add(CPIInterfaceSet *set, CPIInterface *iface)
+{
+ assertNotNull(set, "Parameter set must be non-null");
+ assertNotNull(iface, "Parameter iface must be non-null");
+
+ unsigned ifaceIndex = cpiInterface_GetInterfaceIndex(iface);
+ size_t length = parcArrayList_Size(set->listOfInterfaces);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *listEntry = (CPIInterface *) parcArrayList_Get(set->listOfInterfaces, i);
+ unsigned entryInterfaceIndex = cpiInterface_GetInterfaceIndex(listEntry);
+ if (entryInterfaceIndex == ifaceIndex) {
+ return false;
+ }
+ }
+
+ parcArrayList_Add(set->listOfInterfaces, (PARCObject *) iface);
+ return true;
+}
+
+size_t
+cpiInterfaceSet_Length(const CPIInterfaceSet *set)
+{
+ assertNotNull(set, "Parameter set must be non-null");
+ return parcArrayList_Size(set->listOfInterfaces);
+}
+
+CPIInterface *
+cpiInterfaceSet_GetByOrdinalIndex(CPIInterfaceSet *set, size_t ordinalIndex)
+{
+ assertNotNull(set, "Parameter set must be non-null");
+ return (CPIInterface *) parcArrayList_Get(set->listOfInterfaces, ordinalIndex);
+}
+
+CPIInterface *
+cpiInterfaceSet_GetByInterfaceIndex(const CPIInterfaceSet *set, unsigned interfaceIndex)
+{
+ size_t length = parcArrayList_Size(set->listOfInterfaces);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *listEntry = (CPIInterface *) parcArrayList_Get(set->listOfInterfaces, i);
+ unsigned entryInterfaceIndex = cpiInterface_GetInterfaceIndex(listEntry);
+ if (entryInterfaceIndex == interfaceIndex) {
+ return listEntry;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Uses the system name (e.g. "en0")
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return NULL if not found
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *
+cpiInterfaceSet_GetByName(CPIInterfaceSet *set, const char *name)
+{
+ size_t length = parcArrayList_Size(set->listOfInterfaces);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *listEntry = (CPIInterface *) parcArrayList_Get(set->listOfInterfaces, i);
+ if (cpiInterface_NameEquals(listEntry, name)) {
+ return listEntry;
+ }
+ }
+ return NULL;
+}
+
+bool
+cpiInterfaceSet_Equals(const CPIInterfaceSet *a, const CPIInterfaceSet *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ size_t length_a = parcArrayList_Size(a->listOfInterfaces);
+ size_t length_b = parcArrayList_Size(b->listOfInterfaces);
+
+ if (length_a == length_b) {
+ for (size_t i = 0; i < length_a; i++) {
+ CPIInterface *iface_a = (CPIInterface *) parcArrayList_Get(a->listOfInterfaces, i);
+
+ // the set is unique by interface id, so if it exists in set b, it
+ // exists there by interface id
+ CPIInterface *iface_b = cpiInterfaceSet_GetByInterfaceIndex(b, cpiInterface_GetInterfaceIndex(iface_a));
+ if (!cpiInterface_Equals(iface_b, iface_b)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+const char cpi_InterfaceList[] = "Interfaces";
+
+CPIInterfaceSet *
+cpiInterfaceSet_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpi_InterfaceList);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpi_InterfaceList,
+ parcJSON_ToString(json));
+ PARCJSONArray *ifaceSetJson = parcJSONValue_GetArray(value);
+
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ size_t length = parcJSONArray_GetLength(ifaceSetJson);
+ for (size_t i = 0; i < length; i++) {
+ value = parcJSONArray_GetValue(ifaceSetJson, i);
+ PARCJSON *ifaceJson = parcJSONValue_GetJSON(value);
+ CPIInterface *iface = cpiInterface_FromJson(ifaceJson);
+ cpiInterfaceSet_Add(set, iface);
+ }
+
+ return set;
+}
+
+PARCJSON *
+cpiInterfaceSet_ToJson(CPIInterfaceSet *set)
+{
+ assertNotNull(set, "Parameter must be non-null");
+
+ PARCJSONArray *interfaceList = parcJSONArray_Create();
+
+ size_t length = parcArrayList_Size(set->listOfInterfaces);
+ for (size_t i = 0; i < length; i++) {
+ CPIInterface *iface = (CPIInterface *) parcArrayList_Get(set->listOfInterfaces, i);
+ PARCJSON *json = cpiInterface_ToJson(iface);
+ PARCJSONValue *value = parcJSONValue_CreateFromJSON(json);
+ parcJSON_Release(&json);
+ parcJSONArray_AddValue(interfaceList, value);
+ parcJSONValue_Release(&value);
+ }
+
+ PARCJSON *result = parcJSON_Create();
+ parcJSON_AddArray(result, cpi_InterfaceList, interfaceList);
+ parcJSONArray_Release(&interfaceList);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.h
new file mode 100644
index 00000000..4650c226
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceSet.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceSet.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_InterfaceSet_h
+#define libccnx_cpi_InterfaceSet_h
+
+#include <ccnx/api/control/cpi_Interface.h>
+
+struct cpi_interface_set;
+/**
+ *
+ * @see cpiInterfaceSet_Create
+ */
+typedef struct cpi_interface_set CPIInterfaceSet;
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceSet *cpiInterfaceSet_Create(void);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+void cpiInterfaceSet_Destroy(CPIInterfaceSet **setPtr);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceSet *cpiInterfaceSet_FromJson(PARCJSON *json);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiInterfaceSet_ToJson(CPIInterfaceSet *iface);
+
+/**
+ * Adds interface to set, does not allow duplicates
+ *
+ * Takes ownership of the iface memory if added
+ *
+ * Duplicates are two entries with the same interface index
+ *
+ * @param <#param1#>
+ * @return true if added, false if not (likely a duplicate)
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiInterfaceSet_Add(CPIInterfaceSet *set, CPIInterface *iface);
+
+/**
+ * The number of interfaces in the set
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+size_t cpiInterfaceSet_Length(const CPIInterfaceSet *set);
+
+/**
+ * Uses the ordinal index of the interface in the Set
+ *
+ * Ranges from 0 .. <code>cpiInterfaceSet_Length()-1</code>.
+ *
+ * @param <#param1#>
+ * @return NULL if not found
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *cpiInterfaceSet_GetByOrdinalIndex(CPIInterfaceSet *set, size_t ordinalIndex);
+
+/**
+ * Retreives by the CPI assigned interface index
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return NULL if not found
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *cpiInterfaceSet_GetByInterfaceIndex(const CPIInterfaceSet *set, unsigned interfaceIndex);
+
+/**
+ * Uses the system name (e.g. "en0")
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return NULL if not found
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterface *cpiInterfaceSet_GetByName(CPIInterfaceSet *set, const char *name);
+
+
+/**
+ * Determine if two CPIInterfaceSet instances are equal.
+ *
+ * Two CPIInterfaceSet instances are equal if, and only if, the sets contain the same elements
+ * - order independent.
+ * Each element is compared via <code>cpiInterface_Equals()</code>
+ *
+ * The following equivalence relations on non-null `CPIInterfaceSet` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIInterfaceSet_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `CPIInterfaceSet_Equals(x, y)` must return true if and only if
+ * `cpiInterfaceSet_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiInterfaceSet_Equals(x, y)` returns true and
+ * `cpiInterfaceSet_Equals(y, z)` returns true,
+ * then `cpiInterfaceSet_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiInterfaceSet_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiInterfaceSet_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIInterfaceSet` instance.
+ * @param b A pointer to a `CPIInterfaceSet` instance.
+ * @return true if the two `CPIInterfaceSet` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIInterfaceSet *a = cpiInterfaceSet_Create();
+ * CPIInterfaceSet *b = cpiInterfaceSet_Create();
+ *
+ * if (cpiInterfaceSet_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiInterfaceSet_Equals(const CPIInterfaceSet *a, const CPIInterfaceSet *b);
+#endif // libccnx_cpi_InterfaceSet_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.c b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.c
new file mode 100644
index 00000000..887d5c75
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.c
@@ -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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <LongBow/runtime.h>
+
+typedef enum {
+ CPI_IFACE_LOOPBACK = 1,
+ CPI_IFACE_ETHERNET = 2,
+ CPI_IFACE_LOCALAPP = 3,
+ CPI_IFACE_TUNNEL = 4,
+ CPI_IFACE_GROUP = 5
+} CPIInterfaceType;
+
+typedef enum {
+ CPI_IFACE_UNKNOWN = 0,
+ CPI_IFACE_UP = 1,
+ CPI_IFACE_DOWN = 2
+} CPIInterfaceStateType;
+
+struct cpi_interface_types_string {
+ CPIInterfaceType type;
+ const char *str;
+} interfaceTypesArray[] = {
+ { .type = CPI_IFACE_LOOPBACK, .str = "LOOPBACK" },
+ { .type = CPI_IFACE_ETHERNET, .str = "ETHERNET" },
+ { .type = CPI_IFACE_LOCALAPP, .str = "LOCALAPP" },
+ { .type = CPI_IFACE_TUNNEL, .str = "TUNNEL" },
+ { .type = CPI_IFACE_GROUP, .str = "GROUP" },
+ { .type = 0, .str = NULL }
+};
+
+struct cpi_interface_state_types_string {
+ CPIInterfaceStateType type;
+ const char *str;
+} interfaceStateTypesArray[] = {
+ { .type = CPI_IFACE_UNKNOWN, .str = "UNKNOWN" },
+ { .type = CPI_IFACE_UP, .str = "UP" },
+ { .type = CPI_IFACE_DOWN, .str = "DOWN" },
+ { .type = 0, .str = NULL }
+};
+
+const char *
+cpiInterfaceType_ToString(CPIInterfaceType type)
+{
+ for (int i = 0; interfaceTypesArray[i].str != NULL; i++) {
+ if (interfaceTypesArray[i].type == type) {
+ return interfaceTypesArray[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %d", type);
+}
+
+CPIInterfaceType
+cpiInterfaceType_FromString(const char *str)
+{
+ for (int i = 0; interfaceTypesArray[i].str != NULL; i++) {
+ if (strcasecmp(interfaceTypesArray[i].str, str) == 0) {
+ return interfaceTypesArray[i].type;
+ }
+ }
+ // use a LongBow trap here, instead of an assertion followed by abort().
+ assertTrue(0, "Unknown stirng: %s", str);
+ abort();
+}
+
+const char *
+cpiInterfaceStateType_ToString(CPIInterfaceStateType type)
+{
+ for (int i = 0; interfaceStateTypesArray[i].str != NULL; i++) {
+ if (interfaceStateTypesArray[i].type == type) {
+ return interfaceStateTypesArray[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %d", type);
+}
+
+CPIInterfaceStateType
+cpiInterfaceStateType_FromString(const char *str)
+{
+ for (int i = 0; interfaceStateTypesArray[i].str != NULL; i++) {
+ if (strcasecmp(interfaceStateTypesArray[i].str, str) == 0) {
+ return interfaceStateTypesArray[i].type;
+ }
+ }
+ assertTrue(0, "Unknown stirng: %s", str);
+ abort();
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.h b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.h
new file mode 100644
index 00000000..17587a64
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_InterfaceType.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_InterfaceTypes.h
+ * @brief CPI INterface Types
+ *
+ *
+ */
+#ifndef libccnx_cpi_InterfaceTypes_h
+#define libccnx_cpi_InterfaceTypes_h
+
+typedef enum {
+ CPI_IFACE_LOOPBACK = 1,
+ CPI_IFACE_ETHERNET = 2,
+ CPI_IFACE_LOCALAPP = 3,
+ CPI_IFACE_TUNNEL = 4,
+ CPI_IFACE_GROUP = 5
+} CPIInterfaceType;
+
+typedef enum {
+ CPI_IFACE_UNKNOWN = 0,
+ CPI_IFACE_UP = 1,
+ CPI_IFACE_DOWN = 2
+} CPIInterfaceStateType;
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param [in] type An instance of `CPIInterfaceType`.
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiInterfaceType_ToString(CPIInterfaceType type);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param str A nul-terminated C string.
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceType cpiInterfaceType_FromString(const char *str);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param type A CPIInterfaceStateType value.
+ * @return A nul-terminated C string.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiInterfaceStateType_ToString(CPIInterfaceStateType type);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * <#Discussion#>
+ *
+ * @param str A nul-terminated C string.
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceStateType cpiInterfaceStateType_FromString(const char *str);
+#endif // libccnx_cpi_InterfaceTypes_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Listener.c b/libccnx-transport-rta/ccnx/api/control/cpi_Listener.c
new file mode 100644
index 00000000..d8b75f9d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Listener.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <ccnx/api/control/cpi_Listener.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_JSON.h>
+
+#include <ccnx/api/control/controlPlaneInterface.h>
+extern uint64_t cpi_GetNextSequenceNumber(void);
+
+// JSON keys
+static const char *KEY_IFNAME = "IFNAME";
+static const char *KEY_SYMBOLIC = "SYMBOLIC";
+static const char *KEY_ETHERTYPE = "ETHERTYPE";
+
+static const char *KEY_IP_PROTOCOL = "IPROTO";
+static const char *KEY_ADDR = "ADDR";
+
+static const char *KEY_ADDLISTENER = "AddListener";
+static const char *KEY_REMOVELISTENER = "RemoveListener";
+
+typedef enum {
+ CPIListenerMode_ETHER,
+ CPIListenerMode_IP
+} _CPIListenerMode;
+
+struct cpi_listener {
+ _CPIListenerMode mode;
+ char *symbolic;
+
+ char *interfaceName;
+ uint16_t ethertype;
+
+ CPIAddress *address;
+ CPIInterfaceIPTunnelType type;
+};
+
+CPIListener *
+cpiListener_CreateEther(const char *interfaceName, uint16_t ethertype, const char *symbolic)
+{
+ assertNotNull(interfaceName, "Parameter interfaceName must be non-null");
+ assertNotNull(symbolic, "Parameter symbolic must be non-null");
+
+ CPIListener *listener = parcMemory_AllocateAndClear(sizeof(CPIListener));
+ if (listener) {
+ listener->mode = CPIListenerMode_ETHER;
+ listener->interfaceName = parcMemory_StringDuplicate(interfaceName, strlen(interfaceName));
+ listener->symbolic = parcMemory_StringDuplicate(symbolic, strlen(symbolic));
+ listener->ethertype = ethertype;
+ }
+
+ return listener;
+}
+
+CPIListener *
+cpiListener_CreateIP(CPIInterfaceIPTunnelType type, CPIAddress *localAddress, const char *symbolic)
+{
+ assertNotNull(localAddress, "Parameter peerLinkAddress must be non-null");
+ assertNotNull(symbolic, "Parameter symbolic must be non-null");
+
+ CPIListener *listener = parcMemory_AllocateAndClear(sizeof(CPIListener));
+ if (listener) {
+ listener->mode = CPIListenerMode_IP;
+ listener->type = type;
+ listener->symbolic = parcMemory_StringDuplicate(symbolic, strlen(symbolic));
+ listener->address = cpiAddress_Copy(localAddress);
+ }
+
+ return listener;
+}
+
+void
+cpiListener_Release(CPIListener **listenerPtr)
+{
+ assertNotNull(listenerPtr, "Parameter listenerPtr must be non-null double pointer");
+ assertNotNull(*listenerPtr, "Parameter listenerPtr dereference to non-null pointer");
+
+ CPIListener *listener = *listenerPtr;
+
+ if (listener->symbolic) {
+ parcMemory_Deallocate((void **) &listener->symbolic);
+ }
+
+ if (listener->interfaceName) {
+ parcMemory_Deallocate((void **) &listener->interfaceName);
+ }
+
+ if (listener->address) {
+ cpiAddress_Destroy(&listener->address);
+ }
+
+ parcMemory_Deallocate((void **) &listener);
+ *listenerPtr = NULL;
+}
+
+bool
+cpiListener_Equals(const CPIListener *a, const CPIListener *b)
+{
+ if ((a == NULL && b == NULL) || a == b) {
+ // both null or identically equal
+ return true;
+ }
+
+ if (a == NULL || b == NULL) {
+ // only one is null
+ return false;
+ }
+
+ bool equals = false;
+ if (a->mode == b->mode) {
+ if (strcmp(a->symbolic, b->symbolic) == 0) {
+ if (a->mode == CPIListenerMode_ETHER) {
+ if (a->ethertype == b->ethertype) {
+ if (strcmp(a->interfaceName, b->interfaceName) == 0) {
+ equals = true;
+ }
+ }
+ } else {
+ if (a->type == b->type) {
+ if (cpiAddress_Equals(a->address, b->address)) {
+ equals = true;
+ }
+ }
+ }
+ }
+ }
+
+ return equals;
+}
+
+static void
+_encodeEther(const CPIListener *listener, PARCJSON *json)
+{
+ // ------ Interface Name
+ parcJSON_AddString(json, KEY_IFNAME, listener->interfaceName);
+
+ // ------ EtherType
+ parcJSON_AddInteger(json, KEY_ETHERTYPE, listener->ethertype);
+
+ // ------ Symbolic Name
+ parcJSON_AddString(json, KEY_SYMBOLIC, listener->symbolic);
+}
+
+static void
+_encodeIP(const CPIListener *listener, PARCJSON *json)
+{
+ // ------ Tunnel Type
+ const char *str = cpiInterfaceIPTunnel_TypeToString(listener->type);
+ parcJSON_AddString(json, KEY_IP_PROTOCOL, str);
+
+ // ------ Address
+ PARCJSON *addressJson = cpiAddress_ToJson(listener->address);
+ parcJSON_AddObject(json, KEY_ADDR, addressJson);
+ parcJSON_Release(&addressJson);
+
+ // ------ Symbolic Name
+ parcJSON_AddString(json, KEY_SYMBOLIC, listener->symbolic);
+}
+
+
+
+static PARCJSON *
+_cpiListener_ToJson(const CPIListener *listener)
+{
+ PARCJSON *json = parcJSON_Create();
+
+ if (listener->mode == CPIListenerMode_ETHER) {
+ _encodeEther(listener, json);
+ } else {
+ _encodeIP(listener, json);
+ }
+
+ return json;
+}
+
+/*
+ * We want to create a JSON object that looks like this, where the operationName is either
+ * AddListener or RemoveListener.
+ *
+ * {
+ * "CPI_REQUEST" :
+ * { "SEQUENCE" : <sequence number>,
+ * <operationName> : { "IFNAME" : "em1", "SYMBOLIC" : "conn0", "PEER_ADDR" : { "ADDRESSTYPE" : "LINK", "DATA" : "AQIDBAUG" }, "ETHERTYPE" : 2049 },
+ * }
+ * }
+ */
+static CCNxControl *
+_cpiListener_CreateControlMessage(const CPIListener *listener, const char *operationName)
+{
+ PARCJSON *cpiRequest = parcJSON_Create();
+
+ // --- add the seqnum
+
+ uint64_t seqnum = cpi_GetNextSequenceNumber();
+ parcJSON_AddInteger(cpiRequest, "SEQUENCE", (int) seqnum);
+
+ // -- Add the operation
+
+ PARCJSON *operation = _cpiListener_ToJson(listener);
+ parcJSON_AddObject(cpiRequest, operationName, operation);
+ parcJSON_Release(&operation);
+
+ // -- Do the final encapusulation
+
+ PARCJSON *final = parcJSON_Create();
+ parcJSON_AddObject(final, cpiRequest_GetJsonTag(), cpiRequest);
+
+ // -- Create the CPIControlMessage
+ char *finalString = parcJSON_ToString(final);
+
+ parcJSON_Release(&cpiRequest);
+ parcJSON_Release(&final);
+
+ PARCJSON *oldJson = parcJSON_ParseString(finalString);
+ CCNxControl *result = ccnxControl_CreateCPIRequest(oldJson);
+ parcJSON_Release(&oldJson);
+
+ parcMemory_Deallocate((void **) &finalString);
+
+ return result;
+}
+
+CCNxControl *
+cpiListener_CreateAddMessage(const CPIListener *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ CCNxControl *control = _cpiListener_CreateControlMessage(etherConn, KEY_ADDLISTENER);
+ return control;
+}
+
+CCNxControl *
+cpiListener_CreateRemoveMessage(const CPIListener *etherConn)
+{
+ assertNotNull(etherConn, "Parameter etherConn must be non-null");
+ CCNxControl *control = _cpiListener_CreateControlMessage(etherConn, KEY_REMOVELISTENER);
+ return control;
+}
+
+static bool
+_cpiListener_IsMessageType(const CCNxControl *control, const char *operationName)
+{
+ bool isOperation = false;
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *oldJson = ccnxControl_GetJson(control);
+ PARCJSONValue *value = parcJSON_GetValueByName(oldJson, cpiRequest_GetJsonTag());
+ if (value != NULL) {
+ // the second array element is the key we're looking for
+ PARCJSON *innerJson = parcJSONValue_GetJSON(value);
+ PARCJSONPair *opPair = parcJSON_GetPairByIndex(innerJson, 1);
+ PARCBuffer *sBuf = parcJSONPair_GetName(opPair);
+ const char *operation = parcBuffer_Overlay(sBuf, 0);
+ if (operation && strcasecmp(operation, operationName) == 0) {
+ isOperation = true;
+ }
+ }
+ }
+
+ return isOperation;
+}
+
+bool
+cpiListener_IsAddMessage(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ return _cpiListener_IsMessageType(control, KEY_ADDLISTENER);
+}
+
+bool
+cpiListener_IsRemoveMessage(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ return _cpiListener_IsMessageType(control, KEY_REMOVELISTENER);
+}
+
+static CPIListener *
+_parseEther(PARCJSON *json)
+{
+ PARCJSONValue *value = parcJSON_GetValueByName(json, KEY_IFNAME);
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ const char *ifname = parcBuffer_Overlay(sBuf, 0);
+
+ value = parcJSON_GetValueByName(json, KEY_SYMBOLIC);
+ sBuf = parcJSONValue_GetString(value);
+ const char *symbolic = parcBuffer_Overlay(sBuf, 0);
+
+ value = parcJSON_GetValueByName(json, KEY_ETHERTYPE);
+ int ethertype = (int) parcJSONValue_GetInteger(value);
+
+ CPIListener *listener = cpiListener_CreateEther(ifname, (uint16_t) ethertype, symbolic);
+ return listener;
+}
+
+static CPIListener *
+_parseIP(PARCJSON *json)
+{
+ PARCJSONValue *value = parcJSON_GetValueByName(json, KEY_ADDR);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ KEY_ADDR,
+ parcJSON_ToString(json));
+ PARCJSON *addrJson = parcJSONValue_GetJSON(value);
+
+ CPIAddress *address = cpiAddress_CreateFromJson(addrJson);
+ assertNotNull(address, "Failed to decode the address from %s", parcJSON_ToString(addrJson));
+
+ value = parcJSON_GetValueByName(json, KEY_SYMBOLIC);
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ const char *symbolic = parcBuffer_Overlay(sBuf, 0);
+
+ value = parcJSON_GetValueByName(json, KEY_IP_PROTOCOL);
+ sBuf = parcJSONValue_GetString(value);
+ const char *typeString = parcBuffer_Overlay(sBuf, 0);
+
+ CPIInterfaceIPTunnelType type = cpiInterfaceIPTunnel_TypeFromString(typeString);
+
+ CPIListener *listener = cpiListener_CreateIP(type, address, symbolic);
+ cpiAddress_Destroy(&address);
+
+ return listener;
+}
+
+
+CPIListener *
+cpiListener_FromControl(const CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+
+ CPIListener *listener = NULL;
+
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *oldJson = ccnxControl_GetJson(control);
+ PARCJSONValue *value = parcJSON_GetValueByName(oldJson, cpiRequest_GetJsonTag());
+
+ if (value != NULL) {
+ PARCJSON *innerJson = parcJSONValue_GetJSON(value);
+ // the second array element is the key we're looking for
+ value = parcJSON_GetValueByName(innerJson, KEY_ADDLISTENER);
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(innerJson, KEY_REMOVELISTENER);
+ }
+ if (value != NULL) {
+ PARCJSON *operationJson = parcJSONValue_GetJSON(value);
+
+ // if it has an interface name it's an ether
+ value = parcJSON_GetValueByName(operationJson, KEY_IFNAME);
+ if (value != NULL) {
+ listener = _parseEther(operationJson);
+ } else {
+ listener = _parseIP(operationJson);
+ }
+ }
+ }
+ }
+
+ return listener;
+}
+
+const char *
+cpiListener_GetInterfaceName(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->interfaceName;
+}
+
+const char *
+cpiListener_GetSymbolicName(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->symbolic;
+}
+
+CPIAddress *
+cpiListener_GetAddress(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->address;
+}
+
+uint16_t
+cpiListener_GetEtherType(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->ethertype;
+}
+
+bool
+cpiListener_IsEtherEncap(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->mode == CPIListenerMode_ETHER;
+}
+
+
+bool
+cpiListener_IsIPEncap(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return listener->mode == CPIListenerMode_IP;
+}
+
+bool
+cpiListener_IsProtocolUdp(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return (listener->mode == CPIListenerMode_IP && listener->type == IPTUN_UDP);
+}
+
+bool
+cpiListener_IsProtocolTcp(const CPIListener *listener)
+{
+ assertNotNull(listener, "Parameter listener must be non-null");
+ return (listener->mode == CPIListenerMode_IP && listener->type == IPTUN_TCP);
+}
+
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_Listener.h b/libccnx-transport-rta/ccnx/api/control/cpi_Listener.h
new file mode 100644
index 00000000..13cf42ce
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_Listener.h
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_Listener.h
+ * @brief Represents a protocol listener
+ *
+ * A protocol listener is the tuple (protocol, local address), where protocol is one
+ * of TCP, UDP, Ether, etc., local address is a CPI address. For IP protocols,
+ * local address is an (ip address, port) pair. For Ethernet, it is a (mac address, ethertype) pair.
+ *
+ */
+
+#ifndef CCNx_Control_API_cpi_Listener_h
+#define CCNx_Control_API_cpi_Listener_h
+
+struct cpi_listener;
+typedef struct cpi_listener CPIListener;
+
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+
+/**
+ * Creates a CPIListener object
+ *
+ * The symbolic name represents this listener and may be used by other commands. It must be
+ * unique, otherwise the command will fail when sent to the forwarder.
+ *
+ * @param [in] interfaceName The name of the local interface
+ * @param [in] ethertype The ethertype to use (host byte order)
+ * @param [in] symbolic The user-defined symbolic name
+ *
+ * @return non-null An Allocated object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ * cpiListener_Release(&listener);
+ * }
+ * @endcode
+ */
+CPIListener *cpiListener_CreateEther(const char *interfaceName, uint16_t ethertype, const char *symbolic);
+
+/**
+ * Creates a CPIListener object
+ *
+ * The symbolic name represents this connection and may be used by other commands. It must be
+ * unique, otherwise the command will fail when sent to the forwarder. IPv4 and IPv6 are differentiated
+ * based on the address.
+ *
+ * @param [in] type The local address encapsulation type
+ * @param [in] localAddress The local address to bind to
+ * @param [in] symbolic The user-defined symbolic name
+ *
+ * @return non-null An Allocated object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * struct sockaddr_in sin;
+ * memset(&sin, 0, sizeof(sin));
+ * sin.sin_family = AF_INET;
+ * sin.sin_port = htons(port);
+ * inet_aton(addressString, &sin.sin_addr);
+ * CPIAddress *address = cpiAddress_CreateFromInet(&sin);
+ * CPIListener *listener = cpiListener_CreateIP(IPTUN_UDP, address, "fido");
+ *
+ * cpiAddress_Destroy(&address);
+ * cpiListener_Release(&listener);
+ * }
+ * @endcode
+ */
+CPIListener *cpiListener_CreateIP(CPIInterfaceIPTunnelType type, CPIAddress *localAddress, const char *symbolic);
+
+/**
+ * Releases a reference count to the object
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in,out] etherConnPtr A pointer to an etherConn object, will be null'd.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ * cpiListener_Release(&listener);
+ * }
+ * @endcode
+ */
+void cpiListener_Release(CPIListener **etherConnPtr);
+
+/**
+ * Determine if two CPIListener instances are equal.
+ *
+ * Two CPIListener instances are equal if, and only if,
+ * they are either both null or both non-null and compare
+ * as equal field-for-field.
+ *
+ * The interface name is case sensitive, so "ETH0" is not the same as "eth0".
+ *
+ *
+ * The following equivalence relations on non-null `CPIListener` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIListener_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiListener_Equals(x, y)` must return true if and only if
+ * `cpiListener_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiListener_Equals(x, y)` returns true and
+ * `cpiListener_Equals(y, z)` returns true,
+ * then `cpiListener_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiListener_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiListener_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIListener` instance.
+ * @param b A pointer to a `CPIListener` instance.
+ * @return true if the two `CPIListener` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIListener *a = cpiListener_Create();
+ * CPIListener *b = cpiListener_Create();
+ *
+ * if (cpiListener_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+
+bool cpiListener_Equals(const CPIListener *a, const CPIListener *b);
+
+/**
+ * Creates a control message to add the listener
+ *
+ * An add message indicates to the forwarder that it should add the listener.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return non-null a CPI control message
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ * CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ * cpiListener_Release(&listener);
+ *
+ * ccnxPortal_Send(portal, control, CCNxStackTimeout_Never);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ */
+CCNxControl *cpiListener_CreateAddMessage(const CPIListener *etherConn);
+
+/**
+ * Creates a control message to remove the connection
+ *
+ * A remove message indicates to the forwarder that it should remove the listener.
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return non-null a CPI control message
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ * CCNxControl *control = cpiListener_CreateRemoveMessage(listener);
+ * cpiListener_Release(&listener);
+ *
+ * ccnxPortal_Send(portal, control, CCNxStackTimeout_Never);
+ * ccnxControl_Release(&control);
+ * }
+ * @endcode
+ */
+CCNxControl *cpiListener_CreateRemoveMessage(const CPIListener *etherConn);
+
+/**
+ * Checks if the control message is an Add command
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] control An allocated CCNxControl message
+ *
+ * @return true Message is an Add command for a Listener
+ * @return false Message is not an Add command for a Listener
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *msg = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ * if (ccnxMetaMessage_IsControl(msg)) {
+ * CCNxControl *control = ccnxMetaMessage_GetControl(msg);
+ * if (cpiListener_IsAddMessage(control)) {
+ * // process an add listener request
+ * }
+ * }
+ * ccnxMetaMessage_Release(&msg);
+ * }
+ * @endcode
+ */
+bool cpiListener_IsAddMessage(const CCNxControl *control);
+
+/**
+ * Checks if the message is a Remove command
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] control A CCNx Control message
+ *
+ * @return true Message is an Remove command for a Listener
+ * @return false Message is not Remove Add command for a Listener
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *msg = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ * if (ccnxMetaMessage_IsControl(msg)) {
+ * CCNxControl *control = ccnxMetaMessage_GetControl(msg);
+ * if (cpiListener_IsRemoveMessage(control)) {
+ * // process a remove listener request
+ * }
+ * }
+ * ccnxMetaMessage_Release(&msg);
+ * }
+ * @endcode
+ */
+bool cpiListener_IsRemoveMessage(const CCNxControl *control);
+
+/**
+ * Creates an object from the control message
+ *
+ * The object does not carry any sense of Add or Remove, that is only part of the
+ * Control message. You must release the object when done.
+ *
+ * @param [in] control A CCNx Control message
+ *
+ * @return non-null An Allocated object
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxMetaMessage *msg = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
+ * if (ccnxMetaMessage_IsControl(msg)) {
+ * CCNxControl *control = ccnxMetaMessage_GetControl(msg);
+ * if (cpiListener_IsRemoveMessage(control)) {
+ * // process a remove listener request
+ * CPIListener *listener = cpiListener_FromControl(control);
+ * ...
+ * cpiListener_Release(&listener);
+ * }
+ * }
+ * ccnxMetaMessage_Release(&msg);
+ * }
+ * @endcode
+ */
+CPIListener *cpiListener_FromControl(const CCNxControl *control);
+
+/**
+ * Determines if the encapsulation is an Ethernet protocol
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @retval true It's Ethernet based
+ * @retval false It's not
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiListener_IsEtherEncap(const CPIListener *listener);
+
+/**
+ * Determines if the encapsulation is an IP-based protocol
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @retval true It's IP based
+ * @retval false It's not
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiListener_IsIPEncap(const CPIListener *listener);
+
+/**
+ * Returns the Ethertype for an Ethernet encapsulation
+ *
+ * The returned value is in host byte order
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @retval 0 Not Ethernet encapsulation
+ * @retval positive The ethertype
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+uint16_t cpiListener_GetEtherType(const CPIListener *listener);
+
+/**
+ * Returns the interface name
+ *
+ * The caller should duplicate the string if it will be stored.
+ *
+ * @param [in] etherConn An allocated CPIListener
+ *
+ * @return non-null The interface name.
+ * @return null An error (or not Ethernet encapsulation)
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiListener_GetInterfaceName(const CPIListener *etherConn);
+
+/**
+ * Returns the symbolic name
+ *
+ * The caller should duplicate the string if it will be stored.
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiListener_GetSymbolicName(const CPIListener *listener);
+
+/**
+ * Returns the address (LINK mac address, or INET or INET6 ip address)
+ *
+ * Returns the local address to use for the listener. The address type is
+ * as appropriate for the encapsulation.
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @return non-null The peer's link address
+ * @return null An error
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIAddress *cpiListener_GetAddress(const CPIListener *listener);
+
+/**
+ * For IP encapsulation, tests if the IP protocol is UDP
+ *
+ * Tests if the IP protocol is UDP. If the protocol is not UDP or the encapsulation
+ * is not IP, returns false.
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @retval true IP protocol is UDP
+ * @retval false Not IP or not IP and UDP
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiListener_IsProtocolUdp(const CPIListener *listener);
+
+/**
+ * For IP encapsulation, tests if the IP protocol is TCP
+ *
+ * Tests if the IP protocol is TCP. If the protocol is not TCP or the encapsulation
+ * is not IP, returns false.
+ *
+ * @param [in] listener An allocated CPIListener
+ *
+ * @retval true IP protocol is TCP
+ * @retval false Not IP or not IP and TCP
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+bool cpiListener_IsProtocolTcp(const CPIListener *listener);
+
+#endif // CCNx_Control_API_cpi_Listener_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.c b/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.c
new file mode 100644
index 00000000..43cc2ae8
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.c
@@ -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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <ccnx/api/control/cpi_ManageCaches.h>
+#include <LongBow/runtime.h>
+
+#include "cpi_private.h"
+
+static const char *cpiCacheStoreOn = "CACHE_STORE_ON";
+static const char *cpiCacheStoreOff = "CACHE_STORE_OFF";
+static const char *cpiCacheServeOn = "CACHE_SERVE_ON";
+static const char *cpiCacheServeOff = "CACHE_SERVE_OFF";
+static const char *cpiCacheClear = "CACHE_CLEAR";
+
+PARCJSON *
+cpiManageChaces_CreateCacheClearRequest()
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiCacheClear, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+
+PARCJSON *
+cpiManageChaces_CreateCacheStoreRequest(bool activate)
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result;
+ if (activate) {
+ result = cpi_CreateRequest(cpiCacheStoreOn, json);
+ } else {
+ result = cpi_CreateRequest(cpiCacheStoreOff, json);
+ }
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+
+PARCJSON *
+cpiManageChaces_CreateCacheServeRequest(bool activate)
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result;
+ if (activate) {
+ result = cpi_CreateRequest(cpiCacheServeOn, json);
+ } else {
+ result = cpi_CreateRequest(cpiCacheServeOff, json);
+ }
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+
+const char *
+cpiManageChaces_CacheStoreOnJsonTag()
+{
+ return cpiCacheStoreOn;
+}
+
+const char *
+cpiManageChaces_CacheStoreOffJsonTag()
+{
+ return cpiCacheStoreOff;
+}
+
+const char *
+cpiManageChaces_CacheServeOnJsonTag()
+{
+ return cpiCacheServeOn;
+}
+
+const char *
+cpiManageChaces_CacheServeOffJsonTag()
+{
+ return cpiCacheServeOff;
+}
+
+const char *
+cpiManageChaces_CacheClearJsonTag()
+{
+ return cpiCacheClear;
+}
+
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.h b/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.h
new file mode 100644
index 00000000..31c039df
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageCaches.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_cpi_ManageCaches_h
+#define libccnx_cpi_ManageCaches_h
+
+
+PARCJSON *cpiManageChaces_CreateCacheStoreRequest(bool activate);
+PARCJSON *cpiManageChaces_CreateCacheServeRequest(bool activate);
+PARCJSON *cpiManageChaces_CreateCacheClearRequest();
+
+//const char *cpiLinks_AddEtherConnectionJasonTag();
+
+const char *cpiManageChaces_CacheStoreOnJsonTag();
+
+const char *cpiManageChaces_CacheStoreOffJsonTag();
+
+const char *cpiManageChaces_CacheServeOnJsonTag();
+
+const char *cpiManageChaces_CacheServeOffJsonTag();
+
+const char *cpiManageChaces_CacheClearJsonTag();
+#endif
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.c b/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.c
new file mode 100644
index 00000000..4e198033
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.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 <stdio.h>
+
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <ccnx/api/control/cpi_InterfaceIPTunnelList.h>
+#include <ccnx/api/control/cpi_ManageLinks.h>
+#include <LongBow/runtime.h>
+
+#include "cpi_private.h"
+
+static const char *cpiInterfaceList = "INTERFACE_LIST";
+static const char *cpiCreateTunnel = "CREATE_TUNNEL";
+static const char *cpiRemoveTunnel = "REMOVE_TUNNEL";
+static const char *cpiConnectionList = "CONNECTION_LIST";
+static const char *cpiSetWldr = "SET_WLDR";
+
+PARCJSON *
+cpiLinks_CreateInterfaceListRequest(void)
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiInterfaceList, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+bool cpiLinks_IsInterfaceListResponse(CCNxControl *control);
+
+CPIInterfaceSet *
+cpiLinks_InterfacesFromControlMessage(CCNxControl *response)
+{
+ PARCJSON *json = ccnxControl_GetJson(response);
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiResponse_GetJsonTag());
+ PARCJSON *inner_json = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(inner_json, cpiLinks_InterfaceListJsonTag());
+ PARCJSON *operation = parcJSONValue_GetJSON(value);
+
+ return cpiInterfaceSet_FromJson(operation);
+}
+
+CPIInterfaceIPTunnel *
+cpiLinks_CreateIPTunnelFromControlMessage(CCNxControl *response)
+{
+ PARCJSON *json = ccnxControl_GetJson(response);
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiRequest_GetJsonTag());
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(json, cpiResponse_GetJsonTag());
+ }
+ PARCJSON *inner_json = parcJSONValue_GetJSON(value);
+ value = parcJSON_GetValueByName(inner_json, cpiLinks_CreateTunnelJsonTag());
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(inner_json, cpiLinks_RemoveTunnelJsonTag());
+ }
+ PARCJSON *operation = parcJSONValue_GetJSON(value);
+ return cpiInterfaceIPTunnel_CreateFromJson(operation);
+}
+
+PARCJSON *
+cpiLinks_CreateConnectionListRequest()
+{
+ PARCJSON *json = parcJSON_Create();
+ PARCJSON *result = cpi_CreateRequest(cpiConnectionList, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+
+CPIConnectionList *
+cpiLinks_ConnectionListFromControlMessage(CCNxControl *response)
+{
+ PARCJSON *json = ccnxControl_GetJson(response);
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpiRequest_GetJsonTag());
+ if (value == NULL) {
+ value = parcJSON_GetValueByName(json, cpiResponse_GetJsonTag());
+ }
+ PARCJSON *inner_json = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(inner_json, cpiLinks_ConnectionListJsonTag());
+ PARCJSON *operation = parcJSONValue_GetJSON(value);
+ return cpiConnectionList_FromJson(operation);
+}
+
+PARCJSON *
+cpiLinks_CreateIPTunnel(const CPIInterfaceIPTunnel *iptun)
+{
+ PARCJSON *tunnelJson = cpiInterfaceIPTunnel_ToJson(iptun);
+ PARCJSON *result = cpi_CreateRequest(cpiCreateTunnel, tunnelJson);
+ parcJSON_Release(&tunnelJson);
+
+ return result;
+}
+
+PARCJSON *
+cpiLinks_RemoveIPTunnel(const CPIInterfaceIPTunnel *iptun)
+{
+ PARCJSON *tunnelJson = cpiInterfaceIPTunnel_ToJson(iptun);
+ PARCJSON *result = cpi_CreateRequest(cpiRemoveTunnel, tunnelJson);
+ parcJSON_Release(&tunnelJson);
+
+ return result;
+}
+
+CCNxControl *
+cpiLinks_SetInterfaceState(unsigned ifidx, CPIInterfaceStateType state)
+{
+ return NULL;
+}
+
+CCNxControl *
+cpiLinks_RemoveInterface(unsigned ifidx)
+{
+ return NULL;
+}
+
+const char *
+cpiLinks_InterfaceListJsonTag()
+{
+ return cpiInterfaceList;
+}
+
+const char *
+cpiLinks_CreateTunnelJsonTag()
+{
+ return cpiCreateTunnel;
+}
+
+const char *
+cpiLinks_RemoveTunnelJsonTag()
+{
+ return cpiRemoveTunnel;
+}
+
+const char *
+cpiLinks_ConnectionListJsonTag()
+{
+ return cpiConnectionList;
+}
+
+PARCJSON *
+cpiLinks_CreateSetWldrRequest(const CPIManageWldr *cpiWldr)
+{
+ PARCJSON *json = cpiManageWldr_ToJson(cpiWldr);
+ PARCJSON *result = cpi_CreateRequest(cpiSetWldr, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
+
+CPIManageWldr *
+cpiLinks_ManageWldrFromControlMessage(CCNxControl *control)
+{
+ assertNotNull(control, "Parameter control must be non-null");
+ PARCJSON *json = ccnxControl_GetJson(control);
+
+ PARCJSONPair *cpiWldrOpPair = cpi_ParseRequest(json);
+
+ PARCJSON *cpiWldrJson = parcJSONValue_GetJSON(parcJSONPair_GetValue(cpiWldrOpPair));
+ CPIManageWldr *cpiWldr = cpiManageWldr_FromJson(cpiWldrJson);
+
+ return cpiWldr;
+
+
+ return cpiWldr;
+}
+
+const char *
+cpiLinks_SetWldrJsonTag()
+{
+ return cpiSetWldr;
+}
+
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.h b/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.h
new file mode 100644
index 00000000..7abd5dcd
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageLinks.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_ManageLinks.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_ManageLinks_h
+#define libccnx_cpi_ManageLinks_h
+
+#include <ccnx/api/control/cpi_ConnectionList.h>
+#include <ccnx/api/control/cpi_ControlMessage.h>
+#include <ccnx/api/control/cpi_ManageWldr.h>
+#include <ccnx/api/control/cpi_InterfaceType.h>
+#include <ccnx/api/control/cpi_InterfaceIPTunnel.h>
+#include <ccnx/api/control/cpi_InterfaceIPTunnelList.h>
+#include <ccnx/api/control/cpi_InterfaceSet.h>
+
+
+/**
+ * Create a control message that asks the forwarder to return a list of connections
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiLinks_CreateConnectionListRequest();
+
+/**
+ * Returns a native object from a control message of connections
+ *
+ * The decoder of the response to <code>cpiLinks_CreateConnectionListRequest()</code>
+ *
+ * @param <#param1#>
+ * @return An allocated object, you must destroy it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIConnectionList *cpiLinks_ConnectionListFromControlMessage(CCNxControl *response);
+
+/**
+ * Generate a request for a list of all interfaces
+ *
+ * The transport should resond with a CPI Response message.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpiLinks_CreateInterfaceListRequest(void);
+
+/**
+ * Parse a control message into a list of interfaces
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIInterfaceSet *cpiLinks_InterfacesFromControlMessage(CCNxControl *response);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiLinks_CreateIPTunnel(const CPIInterfaceIPTunnel *iptun);
+
+PARCJSON *cpiLinks_RemoveIPTunnel(const CPIInterfaceIPTunnel *iptun);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIInterfaceIPTunnel *cpiLinks_CreateIPTunnelFromControlMessage(CCNxControl *response);
+
+/**
+ * Set an interface to UP or DOWN
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *cpiLinks_SetInterfaceState(unsigned ifidx, CPIInterfaceStateType state);
+
+/**
+ * Removes an interface
+ *
+ * If it is a virtual interface created through the ControlPlaneInterface, it
+ * is complete removed.
+ *
+ * Trying to remove a physical interface will result in it going down, but it
+ * might not be removed from the system.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CCNxControl *cpiLinks_RemoveInterface(unsigned ifidx);
+
+/**
+ * The key name for an InterfaceList branch
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Do not free it.
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiLinks_InterfaceListJsonTag();
+
+/**
+ * The string tag used in JSON for a Create Tunnel request
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Do not free it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiLinks_CreateTunnelJsonTag();
+
+/**
+ * The string tag used in JSON for a Remove Tunnel request
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Do not free it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiLinks_RemoveTunnelJsonTag();
+
+/**
+ * The string tag used in JSON for a Connection List request
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Do not free it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiLinks_ConnectionListJsonTag();
+
+/**
+ * The string tag used in JSON to add an Ethernet connection
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return Do not free it
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+const char *cpiLinks_AddEtherConnectionJasonTag();
+
+PARCJSON *cpiLinks_CreateSetWldrRequest(const CPIManageWldr *cpiWldr);
+
+CPIManageWldr *cpiLinks_ManageWldrFromControlMessage(CCNxControl *control);
+
+const char *cpiLinks_SetWldrJsonTag();
+
+#endif // libccnx_cpi_ManageLinks_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.c b/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.c
new file mode 100644
index 00000000..d0799d6b
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.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 <config.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <ccnx/api/control/cpi_ManageWldr.h>
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <LongBow/runtime.h>
+
+#include "cpi_private.h"
+
+static const char *cpiWldrString = "WLDR";
+static const char *cpiWldrConn = "CONN";
+static const char *cpiWldrOn = "ON";
+static const char *cpiWldrOff = "OFF";
+
+struct cpi_manage_wldr {
+ char *connectionId;
+ bool active;
+};
+
+void
+cpiManageWldr_Destroy(CPIManageWldr **cpiWldrPtr)
+{
+ assertNotNull(cpiWldrPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*cpiWldrPtr, "Parameter must dereference to non-null pointer");
+ CPIManageWldr *cpiWldr = *cpiWldrPtr;
+
+ parcMemory_Deallocate((void **) &cpiWldr->connectionId);
+ parcMemory_Deallocate((void **) &cpiWldr);
+
+ *cpiWldrPtr = NULL;
+}
+
+CPIManageWldr *
+cpiManageWldr_Create(bool active, char *conn)
+{
+ CPIManageWldr *cpiWldr = parcMemory_AllocateAndClear(sizeof(cpiWldr));
+ assertNotNull(cpiWldr, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(cpiWldr));
+
+ cpiWldr->connectionId = parcMemory_StringDuplicate(conn, strlen(conn));
+ cpiWldr->active = active;
+
+ return cpiWldr;
+}
+
+char *
+CPIManageWldr_ToString(CPIManageWldr *cpiWldr)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ parcBufferComposer_PutString(composer, cpiManageWldr_GetConnection(cpiWldr));
+
+ parcBufferComposer_PutString(composer, cpiWldrString);
+
+ if (cpiManageWldr_IsActive(cpiWldr)) {
+ parcBufferComposer_PutString(composer, cpiWldrOn);
+ } else {
+ parcBufferComposer_PutString(composer, cpiWldrOff);
+ }
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+ return result;
+}
+
+CPIManageWldr *
+cpiManageWldr_Copy(const CPIManageWldr *original)
+{
+ assertNotNull(original, "Parameter a must be non-null");
+ CPIManageWldr *copy = cpiManageWldr_Create(original->active,
+ parcMemory_StringDuplicate(original->connectionId, strlen(original->connectionId)));
+
+ return copy;
+}
+
+bool
+cpiManageWldr_Equals(const CPIManageWldr *a, const CPIManageWldr *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+ if (a == b) {
+ return true;
+ }
+
+ if ((a->active == b->active) && (strcmp(a->connectionId, b->connectionId) == 0)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+cpiManageWldr_IsActive(const CPIManageWldr *cpiWldr)
+{
+ assertNotNull(cpiWldr, "Parameter must be non-null");
+ return cpiWldr->active;
+}
+
+const char *
+cpiManageWldr_GetConnection(const CPIManageWldr *cpiWldr)
+{
+ assertNotNull(cpiWldr, "Parameter must be non-null");
+ return cpiWldr->connectionId;
+}
+
+
+PARCJSON *
+cpiManageWldr_ToJson(const CPIManageWldr *cpiWldr)
+{
+ PARCJSON *wldrJson = parcJSON_Create();
+
+ parcJSON_AddString(wldrJson, cpiWldrConn, cpiWldr->connectionId);
+
+ if (cpiWldr->active) {
+ parcJSON_AddString(wldrJson, cpiWldrString, cpiWldrOn);
+ } else {
+ parcJSON_AddString(wldrJson, cpiWldrString, cpiWldrOff);
+ }
+
+ return wldrJson;
+}
+CPIManageWldr *
+cpiManageWldr_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter json must be non-null");
+ PARCJSON *cpiWldrJson = json;
+
+ PARCJSONValue *value = parcJSON_GetValueByName(cpiWldrJson, cpiWldrConn);
+ assertNotNull(value, "Couldn't locate tag %s in: %s", cpiWldrConn, parcJSON_ToString(json));
+
+ PARCBuffer *wBuf = parcJSONValue_GetString(value);
+ char *conn = parcBuffer_Overlay(wBuf, 0);
+
+ value = parcJSON_GetValueByName(cpiWldrJson, cpiWldrString);
+ assertNotNull(value, "Couldn't locate tag %s in: %s", cpiWldrString, parcJSON_ToString(json));
+
+ wBuf = parcJSONValue_GetString(value);
+ char *active = parcBuffer_Overlay(wBuf, 0);
+
+ CPIManageWldr *cpiWldr;
+ if (strcmp(active, cpiWldrOn) == 0) {
+ cpiWldr = cpiManageWldr_Create(true, conn);
+ } else {
+ cpiWldr = cpiManageWldr_Create(false, conn);
+ }
+
+ return cpiWldr;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.h b/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.h
new file mode 100644
index 00000000..eae06e5f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_ManageWldr.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_cpi_ManageWldr_h
+#define libccnx_cpi_ManageWldr_h
+
+#include <parc/algol/parc_JSON.h>
+
+struct cpi_manage_wldr;
+/**
+ * @typedef CPIManageWldr
+ */
+typedef struct cpi_manage_wldr CPIManageWldr;
+
+/**
+ */
+
+void cpiManageWldr_Destroy(CPIManageWldr **cpiWldrPtr);
+
+CPIManageWldr *cpiManageWldr_Create(bool active, char *conn);
+
+char *CPIManageWldr_ToString(CPIManageWldr *cpiWldr);
+
+CPIManageWldr *cpiManageWldr_Copy(const CPIManageWldr *original);
+
+bool cpiManageWldr_Equals(const CPIManageWldr *a, const CPIManageWldr *b);
+
+bool cpiManageWldr_IsActive(const CPIManageWldr *cpiWldr);
+
+const char *cpiManageWldr_GetConnection(const CPIManageWldr *cpiWldr);
+
+PARCJSON *cpiManageWldr_ToJson(const CPIManageWldr *cpiWldr);
+
+
+CPIManageWldr *cpiManageWldr_FromJson(PARCJSON *json);
+
+#endif // libccnx_cpi_ManageWldr_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.c b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.c
new file mode 100644
index 00000000..ebde3d19
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.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.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <pthread.h>
+#include <string.h>
+#include <strings.h>
+#include <netinet/in.h>
+
+#include <LongBow/runtime.h>
+
+#include <ccnx/api/control/cpi_NameRouteProtocolType.h>
+
+struct name_route_protocol_type_s {
+ const char *str;
+ CPINameRouteProtocolType type;
+} nameRouteProtocolTypeString[] = {
+ { .str = "LOCAL", .type = cpiNameRouteProtocolType_LOCAL },
+ { .str = "CONNECTED", .type = cpiNameRouteProtocolType_CONNECTED },
+ { .str = "STATIC", .type = cpiNameRouteProtocolType_STATIC },
+ { .str = "ACORN", .type = cpiNameRouteProtocolType_ACORN },
+ { .str = NULL, .type = 0 }
+};
+
+const char *
+cpiNameRouteProtocolType_ToString(CPINameRouteProtocolType type)
+{
+ for (int i = 0; nameRouteProtocolTypeString[i].str != NULL; i++) {
+ if (nameRouteProtocolTypeString[i].type == type) {
+ return nameRouteProtocolTypeString[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %d", type);
+}
+
+CPINameRouteProtocolType
+cpiNameRouteProtocolType_FromString(const char *str)
+{
+ for (int i = 0; nameRouteProtocolTypeString[i].str != NULL; i++) {
+ if (strcasecmp(nameRouteProtocolTypeString[i].str, str) == 0) {
+ return nameRouteProtocolTypeString[i].type;
+ }
+ }
+ trapIllegalValue(type, "Unknown type name: %s", str);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.h b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.h
new file mode 100644
index 00000000..29d75ad4
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteProtocolType.h
@@ -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.
+ */
+
+/**
+ * @file cpi_NameRouteProtocolType.h
+ * @brief Specifies the reason for or creator of a route (i.e. the protocol that created the route)
+ *
+ * A LOCAL route points to an application running on the localhost.
+ *
+ * A CONNECTED route exists because the described destination is directly connected to the localhost. For
+ * example, a route to a link local network name would be CONNECTED.
+ *
+ * A STATIC route is administratively created, such as via the "metis_control" program or via the
+ * configuration file.
+ *
+ * An ACORN route is dynamically created by the ACRON routing protocol.
+ *
+ */
+#ifndef libccnx_cpi_NameRouteProtocolType_h
+#define libccnx_cpi_NameRouteProtocolType_h
+
+
+
+/**
+ * @typedef CPINameRouteProtocolType
+ * @abstract Enumerates the protocol that created a route
+ * @constant cpiNameRouteProtocolType_LOCAL An application running on the localhost
+ * @constant cpiNameRouteProtocolType_CONNECTED A directly connected destination
+ * @constant cpiNameRouteProtocolType_STATIC Administratively created
+ * @constant cpiNameRouteProtocolType_ACORN The ACORN routing protocol
+ */
+typedef enum {
+ cpiNameRouteProtocolType_LOCAL = 0, // local face to app
+ cpiNameRouteProtocolType_CONNECTED = 1, // directly connected network
+ cpiNameRouteProtocolType_STATIC = 2, // administrative static route
+ cpiNameRouteProtocolType_ACORN = 20
+} CPINameRouteProtocolType;
+
+/**
+ * Return the string representation of the specified `CPINameRouteProtocolType`.
+ *
+ * The returned string does not need to be freed.
+ *
+ * @param [in] type The type to represent as a string.
+ *
+ * @return The string representation of the specified 'CPINameRouteProtocolType'.
+ *
+ * Example:
+ * @code
+ * {
+ * CPINameRouteProtocolType type = ROUTE_PROTO_CONNECTED;
+ *
+ * char *name = cpiNameRouteProtocolType_ToString(type);
+ *
+ * printf("NameRouteProtocolType is %s\n", name);
+ * }
+ * @endcode
+ *
+ * @see cpiNameRouteProtocolType_FromString
+ */
+const char *cpiNameRouteProtocolType_ToString(CPINameRouteProtocolType type);
+
+/**
+ * Given a string describing a `CPINameRouteProtocolType`, return the matching `CPINameRouteProtocolType`.
+ *
+ * If an invalid string is specified, the program will terminate with an IllegalValue exception.
+ * Possible values are: "LOCAL", "CONNECTED", "STATIC", and "ACORN".
+ *
+ * @param [in] str A pointer to a string representation of the desired `CPINameRouteProtocolType`.
+ *
+ * @return The `CPINameRouteProtocolType` matching the specified string.
+ *
+ * Example:
+ * @code
+ * {
+ * CPINameRouteProtocolType type = cpiNameRouteProtocolType_FromString("STATIC");
+ * }
+ * @endcode
+ *
+ * @see cpiNameRouteProtocolType_ToString
+ */
+CPINameRouteProtocolType cpiNameRouteProtocolType_FromString(const char *str);
+#endif // libccnx_cpi_NameRouteProtocolType_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.c b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.c
new file mode 100644
index 00000000..44a5d943
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <pthread.h>
+#include <string.h>
+#include <strings.h>
+#include <netinet/in.h>
+
+#include <LongBow/runtime.h>
+
+#include <ccnx/api/control/cpi_NameRouteType.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_Base64.h>
+
+#include "controlPlaneInterface.h"
+#include "cpi_private.h"
+
+struct name_route_type_s {
+ const char *str;
+ CPINameRouteType type;
+} nameRouteTypeString[] = {
+ { .str = "EXACT", .type = cpiNameRouteType_EXACT_MATCH },
+ { .str = "LONGEST", .type = cpiNameRouteType_LONGEST_MATCH },
+ { .str = "DEFAULT", .type = cpiNameRouteType_DEFAULT },
+ { .str = NULL, .type = 0 }
+};
+
+const char *
+cpiNameRouteType_ToString(CPINameRouteType type)
+{
+ for (int i = 0; nameRouteTypeString[i].str != NULL; i++) {
+ if (nameRouteTypeString[i].type == type) {
+ return nameRouteTypeString[i].str;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %d", type);
+}
+
+CPINameRouteType
+cpiNameRouteType_FromString(const char *str)
+{
+ for (int i = 0; nameRouteTypeString[i].str != NULL; i++) {
+ if (strcasecmp(nameRouteTypeString[i].str, str) == 0) {
+ return nameRouteTypeString[i].type;
+ }
+ }
+ trapIllegalValue(type, "Unknown type: %s", str);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.h b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.h
new file mode 100644
index 00000000..a0dca7cf
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_NameRouteType.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 cpi_NameRouteType.h
+ * @brief Specifies how a route should be matched
+ *
+ * NOTE: Only LONGEST_MATCH is currently implemented.
+ *
+ * A LONGEST_MATCH route is a normal CCNx route entry. It will match any Interest name that is equal to the route prefix
+ * or any Interest name that is equal to the router prefix and has additional name components. Each name component must be
+ * exactly equal on a component-by-component basis.
+ *
+ * An EXACT_MATCH route will not match any longer names. An Interest name must exactly match the route prefix.
+ *
+ * A Default route will be used if there are no other matches.
+ *
+ */
+
+#ifndef libccnx_cpi_NameRouteType_h
+#define libccnx_cpi_NameRouteType_h
+
+/**
+ * @typedef CPINameRouteType
+ * @abstract Enumerates the types of route entries
+ * @constant cpiNameRouteType_EXACT_MATCH Specifies an exact match route
+ * @constant cpiNameRouteType_LONGEST_MATCH Specifies a longest matching prefix entry (a normal CCNx route)
+ * @constant cpiNameRouteType_DEFAULT Specifies a default route that is used if no other entries match
+ */
+typedef enum {
+ cpiNameRouteType_EXACT_MATCH = 1,
+ cpiNameRouteType_LONGEST_MATCH = 2,
+ cpiNameRouteType_DEFAULT = 3
+} CPINameRouteType;
+
+/**
+ * Return the string representation of the specified `CPINameRouteType`.
+ *
+ * The returned string does not need to be freed.
+ *
+ * @param [in] type The type to represent as a string.
+ *
+ * @return The string representation of the specified 'CPINameRouteType'.
+ *
+ * Example:
+ * @code
+ * {
+ * CPINameRouteType type = cpiNameRouteType_LONGEST_MATCH;
+ *
+ * char *name = cpiNameRouteType_ToString(type);
+ *
+ * printf("NameRouteType is %s\n", name);
+ * }
+ * @endcode
+ *
+ * @see cpiNameRouteType_FromString
+ */
+const char *cpiNameRouteType_ToString(CPINameRouteType type);
+
+/**
+ * Given a string describing a `CPINameRouteType`, return the matching `CPINameRouteType`.
+ *
+ * If an invalid string is specified, the program will terminate with an IllegalValue exception.
+ * Possible values are: "EXACT", "LONGEST", and "DEFAULT".
+ *
+ * @param [in] str A pointer to a string representation of the desired `CPINameRouteType`.
+ *
+ * @return The `NameRouteType` matching the specified string.
+ *
+ * Example:
+ * @code
+ * {
+ * CPINameRouteType type = cpiNameRouteType_FromString("EXACT");
+ * }
+ * @endcode
+ *
+ * @see cpiNameRouteType_ToString
+ */
+CPINameRouteType cpiNameRouteType_FromString(const char *str);
+#endif // libccnx_cpi_NameRouteType_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.c b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.c
new file mode 100644
index 00000000..0de5c915
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <stdlib.h>
+#include <sys/time.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/api/control/controlPlaneInterface.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <limits.h>
+
+#include <LongBow/runtime.h>
+
+static const char *cpiPrefix = "PREFIX";
+static const char *cpiInterface = "INTERFACE";
+static const char *cpiFlags = "FLAGS";
+static const char *cpiLifetime = "LIFETIME";
+static const char *cpiNexthop = "NEXTHOP";
+static const char *cpiProtocol = "PROTOCOL";
+static const char *cpiRouteType = "ROUTETYPE";
+static const char *cpiCost = "COST";
+static const char *cpiSymbolic = "SYMBOLIC";
+
+struct cpi_route_entry {
+ bool hasInterfaceIndex;
+ unsigned interfaceIndex;
+
+ CCNxName *prefix;
+ char *symbolic;
+ CPIAddress *nexthop;
+ CPINameRouteProtocolType routingProtocol;
+ CPINameRouteType routeType;
+ unsigned cost;
+
+ bool hasLifetime;
+ struct timeval lifetime;
+};
+
+void
+cpiRouteEntry_Destroy(CPIRouteEntry **routeEntryPtr)
+{
+ assertNotNull(routeEntryPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*routeEntryPtr, "Parameter must dereference to non-null pointer");
+ CPIRouteEntry *route = *routeEntryPtr;
+
+ ccnxName_Release(&route->prefix);
+ if (route->nexthop) {
+ cpiAddress_Destroy(&route->nexthop);
+ }
+
+ if (route->symbolic) {
+ parcMemory_Deallocate((void **) &route->symbolic);
+ }
+
+ parcMemory_Deallocate((void **) &route);
+ *routeEntryPtr = NULL;
+}
+
+CPIRouteEntry *
+cpiRouteEntry_CreateRouteToSelf(const CCNxName *prefix)
+{
+ void *optionalLifetime = NULL;
+ void *nexthop = NULL;
+ unsigned cost = 0;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName_Copy(prefix),
+ CPI_CURRENT_INTERFACE,
+ nexthop,
+ cpiNameRouteProtocolType_LOCAL,
+ cpiNameRouteType_LONGEST_MATCH,
+ optionalLifetime,
+ cost);
+ return route;
+}
+
+char *
+cpiRouteEntry_ToString(CPIRouteEntry *route)
+{
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+
+ parcBufferComposer_Format(composer, "%6d %9.9s %7.7s %u ",
+ route->interfaceIndex,
+ cpiNameRouteProtocolType_ToString(route->routingProtocol),
+ cpiNameRouteType_ToString(route->routeType),
+ route->cost);
+
+ if (route->symbolic != NULL) {
+ parcBufferComposer_PutString(composer, route->symbolic);
+ } else {
+ parcBufferComposer_PutChar(composer, '-');
+ }
+
+ if (route->nexthop != NULL) {
+ cpiAddress_BuildString(cpiRouteEntry_GetNexthop(route), composer);
+ } else {
+ parcBufferComposer_PutChar(composer, '-');
+ }
+
+ if (route->hasLifetime) {
+#if __APPLE__
+ parcBufferComposer_Format(composer, " %ld.%06d ", route->lifetime.tv_sec, route->lifetime.tv_usec);
+#else
+ parcBufferComposer_Format(composer, " %ld.%06ld ", route->lifetime.tv_sec, route->lifetime.tv_usec);
+#endif
+ } else {
+ parcBufferComposer_Format(composer, " %8.8s ", "infinite");
+ }
+
+ char *ccnxName = ccnxName_ToString(cpiRouteEntry_GetPrefix(route));
+ parcBufferComposer_PutString(composer, ccnxName);
+
+ parcMemory_Deallocate((void **) &ccnxName);
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *result = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+
+ parcBufferComposer_Release(&composer);
+ return result;
+}
+
+CPIRouteEntry *
+cpiRouteEntry_Create(CCNxName *prefix,
+ unsigned interfaceIndex,
+ const CPIAddress *optionalNexthop,
+ CPINameRouteProtocolType routingProtocol,
+ CPINameRouteType routeType,
+ const struct timeval *optionalLifetime,
+ unsigned cost)
+{
+ assertNotNull(prefix, "Parameter prefix must be non-null");
+
+ CPIRouteEntry *route = parcMemory_AllocateAndClear(sizeof(CPIRouteEntry));
+ assertNotNull(route, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIRouteEntry));
+ route->prefix = prefix;
+ route->symbolic = NULL;
+ route->interfaceIndex = interfaceIndex;
+ route->hasInterfaceIndex = true;
+ route->nexthop = optionalNexthop != NULL ? cpiAddress_Copy(optionalNexthop) : NULL;
+ route->routingProtocol = routingProtocol;
+ route->routeType = routeType;
+ route->cost = cost;
+
+ if (optionalLifetime) {
+ route->hasLifetime = true;
+ route->lifetime = *optionalLifetime;
+ } else {
+ route->hasLifetime = false;
+ route->lifetime = (struct timeval) { .tv_sec = INT_MAX, .tv_usec = 0 };
+ }
+
+ return route;
+}
+
+CPIRouteEntry *
+cpiRouteEntry_CreateSymbolic(CCNxName *prefix,
+ const char *symbolicName,
+ CPINameRouteProtocolType routingProtocol,
+ CPINameRouteType routeType,
+ const struct timeval *optionalLifetime,
+ unsigned cost)
+{
+ assertNotNull(prefix, "Parameter prefix must be non-null");
+
+ CPIRouteEntry *route = parcMemory_AllocateAndClear(sizeof(CPIRouteEntry));
+ assertNotNull(route, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIRouteEntry));
+ route->prefix = prefix;
+ route->symbolic = parcMemory_StringDuplicate(symbolicName, strlen(symbolicName));
+ route->interfaceIndex = UINT32_MAX;
+ route->hasInterfaceIndex = false;
+ route->nexthop = NULL;
+ route->routingProtocol = routingProtocol;
+ route->routeType = routeType;
+ route->cost = cost;
+
+ if (optionalLifetime) {
+ route->hasLifetime = true;
+ route->lifetime = *optionalLifetime;
+ } else {
+ route->hasLifetime = false;
+ route->lifetime = (struct timeval) { .tv_sec = INT_MAX, .tv_usec = 0 };
+ }
+
+ return route;
+}
+
+
+CPIRouteEntry *
+cpiRouteEntry_Copy(const CPIRouteEntry *original)
+{
+ assertNotNull(original, "Parameter a must be non-null");
+ CPIRouteEntry *copy;
+
+ if (original->hasInterfaceIndex) {
+ copy = cpiRouteEntry_Create(
+ ccnxName_Copy(original->prefix),
+ original->interfaceIndex,
+ (original->nexthop ? original->nexthop : NULL),
+ original->routingProtocol,
+ original->routeType,
+ (original->hasLifetime ? &original->lifetime : NULL),
+ original->cost);
+
+ if (original->symbolic) {
+ copy->symbolic = parcMemory_StringDuplicate(original->symbolic, strlen(original->symbolic));
+ }
+ } else {
+ copy = cpiRouteEntry_CreateSymbolic(
+ ccnxName_Copy(original->prefix),
+ original->symbolic,
+ original->routingProtocol,
+ original->routeType,
+ (original->hasLifetime ? &original->lifetime : NULL),
+ original->cost);
+ }
+ return copy;
+}
+
+void
+cpiRouteEntry_SetInterfaceIndex(CPIRouteEntry *route, unsigned interfaceIndex)
+{
+ assertNotNull(route, "Parameter a must be non-null");
+ route->interfaceIndex = interfaceIndex;
+ route->hasInterfaceIndex = true;
+}
+
+
+bool
+cpiRouteEntry_Equals(const CPIRouteEntry *a, const CPIRouteEntry *b)
+{
+ assertNotNull(a, "Parameter a must be non-null");
+ assertNotNull(b, "Parameter b must be non-null");
+ if (a == b) {
+ return true;
+ }
+
+ if (a->interfaceIndex == b->interfaceIndex) {
+ if (a->routeType == b->routeType) {
+ if (a->routingProtocol == b->routingProtocol) {
+ if (a->cost == b->cost) {
+ if (ccnxName_Equals(a->prefix, b->prefix)) {
+ if (cpiAddress_Equals(a->nexthop, b->nexthop)) {
+ if (a->hasLifetime == b->hasLifetime) {
+ if (timercmp(&a->lifetime, &b->lifetime, ==)) {
+ if (a->symbolic == b->symbolic || (a->symbolic != NULL && b->symbolic != NULL && strcasecmp(a->symbolic, b->symbolic) == 0)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+const CCNxName *
+cpiRouteEntry_GetPrefix(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->prefix;
+}
+
+unsigned
+cpiRouteEntry_GetInterfaceIndex(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->interfaceIndex;
+}
+
+const CPIAddress *
+cpiRouteEntry_GetNexthop(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->nexthop;
+}
+
+bool
+cpiRouteEntry_HasLifetime(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->hasLifetime;
+}
+
+struct timeval
+cpiRouteEntry_GetLifetime(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->lifetime;
+}
+
+CPINameRouteProtocolType
+cpiRouteEntry_GetRouteProtocolType(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->routingProtocol;
+}
+
+CPINameRouteType
+cpiRouteEntry_GetRouteType(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->routeType;
+}
+
+unsigned
+cpiRouteEntry_GetCost(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->cost;
+}
+
+const char *
+cpiRouteEntry_GetSymbolicName(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+ return route->symbolic;
+}
+
+PARCJSON *
+cpiRouteEntry_ToJson(const CPIRouteEntry *route)
+{
+ assertNotNull(route, "Parameter must be non-null");
+
+ PARCJSON *routeJson = parcJSON_Create();
+ char *uri = ccnxName_ToString(route->prefix);
+ parcJSON_AddString(routeJson, cpiPrefix, uri);
+ parcMemory_Deallocate((void **) &uri);
+
+ if (route->symbolic) {
+ parcJSON_AddString(routeJson, cpiSymbolic, route->symbolic);
+ }
+
+ if (route->hasInterfaceIndex) {
+ parcJSON_AddInteger(routeJson, cpiInterface, route->interfaceIndex);
+ }
+
+ parcJSON_AddInteger(routeJson, cpiFlags, 0);
+
+ if (route->nexthop) {
+ // some registrations can have NULL nexthop, its ok
+ PARCJSON *json = cpiAddress_ToJson(route->nexthop);
+ parcJSON_AddObject(routeJson, cpiNexthop, json);
+ parcJSON_Release(&json);
+ }
+
+ parcJSON_AddString(routeJson, cpiProtocol, cpiNameRouteProtocolType_ToString(route->routingProtocol));
+ parcJSON_AddString(routeJson, cpiRouteType, cpiNameRouteType_ToString(route->routeType));
+ parcJSON_AddInteger(routeJson, cpiCost, route->cost);
+
+ if (route->hasLifetime) {
+ PARCJSONArray *lifetimeJson = parcJSONArray_Create();
+ PARCJSONValue *value = parcJSONValue_CreateFromInteger(route->lifetime.tv_sec);
+ parcJSONArray_AddValue(lifetimeJson, value);
+ parcJSONValue_Release(&value);
+ value = parcJSONValue_CreateFromInteger(route->lifetime.tv_usec);
+ parcJSONArray_AddValue(lifetimeJson, value);
+ parcJSONValue_Release(&value);
+ parcJSON_AddArray(routeJson, cpiLifetime, lifetimeJson);
+ parcJSONArray_Release(&lifetimeJson);
+ }
+
+ return routeJson;
+}
+
+CPIRouteEntry *
+cpiRouteEntry_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter json must be non-null");
+ PARCJSON *routeJson = json;
+
+ PARCJSONValue *value = parcJSON_GetValueByName(routeJson, cpiPrefix);
+ assertNotNull(value, "Couldn't locate tag %s in: %s", cpiPrefix, parcJSON_ToString(json));
+ PARCBuffer *sBuf = parcJSONValue_GetString(value);
+ CCNxName *prefix = ccnxName_CreateFromCString(parcBuffer_Overlay(sBuf, 0));
+
+ const char *symbolicName = NULL;
+ value = parcJSON_GetValueByName(routeJson, cpiSymbolic);
+ if (value) {
+ sBuf = parcJSONValue_GetString(value);
+ symbolicName = parcBuffer_Overlay(sBuf, 0);
+ }
+
+ bool hasInterfaceIndex = false;
+ unsigned interfaceIndex = UINT32_MAX;
+ value = parcJSON_GetValueByName(routeJson, cpiInterface);
+ if (value) {
+ hasInterfaceIndex = true;
+ interfaceIndex = (unsigned) parcJSONValue_GetInteger(value);
+ }
+
+ CPIAddress *nexthop = NULL;
+ value = parcJSON_GetValueByName(routeJson, cpiNexthop);
+ if (value != NULL) {
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Json key %s wrong type in json %s",
+ cpiNexthop,
+ parcJSON_ToString(json));
+ PARCJSON *nexthopJson = parcJSONValue_GetJSON(value);
+ nexthop = cpiAddress_CreateFromJson(nexthopJson);
+ }
+
+ value = parcJSON_GetValueByName(routeJson, cpiProtocol);
+ sBuf = parcJSONValue_GetString(value);
+ char *valueString = parcBuffer_Overlay(sBuf, 0);
+ CPINameRouteProtocolType routingProtocol = cpiNameRouteProtocolType_FromString(valueString);
+
+ value = parcJSON_GetValueByName(routeJson, cpiRouteType);
+ sBuf = parcJSONValue_GetString(value);
+ valueString = parcBuffer_Overlay(sBuf, 0);
+ CPINameRouteType routingType = cpiNameRouteType_FromString(valueString);
+
+ value = parcJSON_GetValueByName(routeJson, cpiCost);
+ unsigned cost = (unsigned) parcJSONValue_GetInteger(value);
+
+ value = parcJSON_GetValueByName(routeJson, cpiLifetime);
+ struct timeval *lifetime = NULL;
+
+ struct timeval actual_time = { 0, 0 };
+ if (value != NULL) {
+ assertTrue(parcJSONValue_IsArray(value),
+ "Json key %s wrong typein json %s",
+ cpiNexthop,
+ parcJSON_ToString(json));
+ PARCJSONArray *lifetimeJson = parcJSONValue_GetArray(value);
+
+ actual_time.tv_sec = parcJSONValue_GetInteger(parcJSONArray_GetValue(lifetimeJson, 0));
+ actual_time.tv_usec = (int) parcJSONValue_GetInteger(parcJSONArray_GetValue(lifetimeJson, 1));
+
+ lifetime = &actual_time;
+ }
+
+
+ // == we're now ready to create the object
+ CPIRouteEntry *route;
+ if (symbolicName) {
+ route = cpiRouteEntry_CreateSymbolic(prefix, symbolicName, routingProtocol, routingType, lifetime, cost);
+ if (hasInterfaceIndex) {
+ cpiRouteEntry_SetInterfaceIndex(route, interfaceIndex);
+ }
+ } else {
+ route = cpiRouteEntry_Create(prefix, interfaceIndex, nexthop, routingProtocol, routingType, lifetime, cost);
+ }
+
+ if (nexthop) {
+ cpiAddress_Destroy(&nexthop);
+ }
+
+ return route;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.h b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.h
new file mode 100644
index 00000000..79aa2cfb
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntry.h
@@ -0,0 +1,587 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_RouteEntry.h
+ * @brief A representation of a route entry.
+ *
+ * A CCNx route consists of the tuple (prefix, interfaceIndex, [nextHop], routingProtocol, routeType, [lifetime], cost).
+ *
+ * The "prefix" is the CCNx name in question. If the "routeType" is Exact Match then the prefix must exactly match an Interest Name.
+ * If the routeType is Longest Prefix (a normal CCNx route), then it will match any equal or longer Interest name. If the routeType
+ * is Default, then it will match any equal or longer name if no other route matched.
+ *
+ * The interfaceIndex (a.k.a Connection ID) is the entry in the forwarder's connection table to use to forward the Interest.
+ * Newer commands use a symblic name instead of a connection id. A symbolic name is an alpha followed by alphanums. It is specified
+ * when creating a tunnel or connection. Auto-added connections inside the forwarder will only have a connection id.
+ *
+ * The optional NextHop specifies a link-specific nexthop identifier on the outbound interfaceIndex. This could be used, for example, with
+ * an Ethernet link. The Connection table entry could be the CCNx Group address entry (i.e. any packet sent to it will go out on the
+ * CCNx Ethernet group address) and by specifying the optional NextHop give a specific unicast MAC address.
+ *
+ * routingProtocol identifies the protocol that created the route entry.
+ *
+ * routeType, as described above, specifies how the prefix matches an Interest name.
+ *
+ * lifetime specified how long the router will keep the forwarding entry active. The routing protocol must refresh the entry
+ * to keep it alive.
+ *
+ * cost reflects the route cost. Some forwarding strategies might use the cost information to make a decision, but it is not
+ * used by the normal unicast or multicast strategies.
+ *
+ */
+#ifndef libccnx_cpi_RouteEntry_h
+#define libccnx_cpi_RouteEntry_h
+
+#include <ccnx/common/ccnx_Name.h>
+#include <ccnx/api/control/cpi_Address.h>
+#include <ccnx/api/control/cpi_NameRouteProtocolType.h>
+#include <ccnx/api/control/cpi_NameRouteType.h>
+
+#include <parc/algol/parc_JSON.h>
+
+struct cpi_route_entry;
+/**
+ * @typedef CPIRouteEntry
+ * @brief A representation of a route entry.
+ */
+typedef struct cpi_route_entry CPIRouteEntry;
+
+/**
+ * Creates a route entry, takes ownership of the name object
+ *
+ * @param [in] prefix for the route, takes ownership of this memory.
+ * @param [in] optionalNexthop may be NULL, represents where the FIB points.
+ * @param [in] routingProtocol represents the algorithm used to create the route entry.
+ * @param [in] routeType selects how the FIB matches the prefix.
+ * @param [in] optionalLifetime is how long the FIB entry stays valid unless refreshed.
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A pointer to a valid CPIRouteEntry instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_Destroy
+ */
+CPIRouteEntry *cpiRouteEntry_Create(CCNxName *prefix, unsigned interfaceIndex, const CPIAddress *optionalNexthop,
+ CPINameRouteProtocolType routingProtocol, CPINameRouteType routeType,
+ const struct timeval *optionalLifetime, unsigned cost);
+
+
+/**
+ * Creates a route entry, takes ownership of the name object
+ *
+ * @param [in] prefix for the route, takes ownership of this memory.
+ * @param [in] symbolicName The symbolic name of the connection or tunnel to use.
+ * @param [in] routingProtocol represents the algorithm used to create the route entry.
+ * @param [in] routeType selects how the FIB matches the prefix.
+ * @param [in] optionalLifetime is how long the FIB entry stays valid unless refreshed.
+ *
+ * @return NULL An error occurred.
+ * @return non-NULL A pointer to a valid CPIRouteEntry instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_CreateSymbolic(prefix, "tun0", cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_Destroy
+ */
+CPIRouteEntry *cpiRouteEntry_CreateSymbolic(CCNxName *prefix, const char *symbolicName,
+ CPINameRouteProtocolType routingProtocol, CPINameRouteType routeType,
+ const struct timeval *optionalLifetime, unsigned cost);
+
+/**
+ * Create a CPIRouteEntry instance that represents a route to this node.
+ *
+ * @param [in] name A CCNxName instance representing the name to route to this node.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to an allocated CPIRouteEntry instance that must be deallocated via cpiRouteEntry_Destroy.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_FromCString("lci:/a/b");
+ * CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(prefix);
+ * ccnxName_Release(&prefix);
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_Destroy
+ */
+CPIRouteEntry *cpiRouteEntry_CreateRouteToSelf(const CCNxName *name);
+
+/**
+ * Deallocate and destroy a CPIRouteEntry instance.
+ *
+ * @param [in] routeEntryPtr A pointer to a pointer to a valid CPIRouteEntry instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_FromCString("lci:/a/b");
+ * CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(prefix);
+ * ccnxName_Release(&prefix);
+ *
+ * cpiRouteEntry_Destroy(&route);
+ * }
+ * @endcode
+ */
+void cpiRouteEntry_Destroy(CPIRouteEntry **routeEntryPtr);
+
+/**
+ * Copy a CPIRouteEntry instance.
+ *
+ * Creates a deep copy of the Route Entry.
+ *
+ * @param [in] routeEntry A pointer to a valid CPIRouteEntry instance.
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid CPIRouteEntry instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_FromCString("lci:/a/b");
+ * CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(prefix);
+ * ccnxName_Release(&prefix);
+ *
+ * CPIRouteEntry *copy = cpiRouteEntry_Copy(route);
+ * cpiRouteEntry_Destroy(&route);
+ * cpiRouteEntry_Destroy(&copy);
+ * }
+ * @endcode
+ */
+CPIRouteEntry *cpiRouteEntry_Copy(const CPIRouteEntry *routeEntry);
+
+/**
+ * Determine if two `CPIRouteEntry` instances are equal.
+ *
+ * The following equivalence relations on non-null `CPIRouteEntry` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `cpiRouteEntry_Equals(x, x)` must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y, `cpiRouteEntry_Equals(x, y)` must return true if and only if
+ * `cpiRouteEntry_Equals(y x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiRouteEntry_Equals(x, y)` returns true and
+ * `cpiRouteEntry_Equals(y, z)` returns true,
+ * then `cpiRouteEntry_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple invocations of `cpiRouteEntry_Equals(x, y)`
+ * consistently return true or consistently return false.
+ *
+ * * For any non-null reference value x, `cpiRouteEntry_Equals(x, NULL)` must return false.
+ *
+ *
+ * @param [in] a A pointer to a `CPIRouteEntry` instance.
+ * @param [in] b A pointer to a `CPIRouteEntry` instance.
+ *
+ * @return true `CPIRouteEntry` x and y are equal.
+ * @return false `CPIRouteEntry` x and y are not equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIRouteEntry *bufferA = parcBuffer_Allocate(10);
+ * CPIRouteEntry *bufferB = parcBuffer_Allocate(10);
+ *
+ * CCNxName *prefixA = ccnxName_FromCString("lci:/a/b");
+ * CPIRouteEntry *routeA = cpiRouteEntry_CreateRouteToSelf(prefixA);
+ * ccnxName_Release(&prefixA);
+ *
+ * CCNxName *prefixB = ccnxName_FromCString("lci:/a/b");
+ * CPIRouteEntry *routeB = cpiRouteEntry_CreateRouteToSelf(prefixB);
+ * ccnxName_Release(&prefixB);
+ *
+ * if (cpiRouteEntry_Equals(routeA, routeB)) {
+ * printf("Routes are equal.\n");
+ * } else {
+ * printf("Routes are NOT equal.\n");
+ * }
+ * cpiRouteEntry_Destroy(&bufferA);
+ * cpiRouteEntry_Destroy(&bufferB);
+ * }
+ * @endcode
+ */
+bool cpiRouteEntry_Equals(const CPIRouteEntry *a, const CPIRouteEntry *b);
+
+/**
+ * Set the interface index for the given `CPIRouteEntry`
+ *
+ * @param [in] route A pointer to a `CPIRouteEntry` instance.
+ * @param [in] interfaceIndex The interface index value.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = -1; // unknown
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval *lifetimePtr = NULL;
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetimePtr, cost);
+ *
+ * // once we know an interface index, set it
+ * cpiRouteEntry_SetInterfaceIndex(route, 55);
+ * }
+ * @endcode
+ */
+void cpiRouteEntry_SetInterfaceIndex(CPIRouteEntry *route, unsigned interfaceIndex);
+
+/**
+ * Get the name of the routing prefix in the given `CPIRouteEntry` instance.
+ *
+ * @param [in] route A pointer to a `CPIRouteEntry` instance.
+ *
+ * @return A pointer to the name of the routing prefix in the given `CPIRouteEntry` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = -1; // unknown
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval *lifetimePtr = NULL;
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetimePtr, cost);
+ *
+ * CCNxName *testPrefix = cpiRouteEntry_GetPrefix(route);
+ * assertTrue(ccnxName_Equals(prefix, testPrefix), "The prefix of the route should be equal to what was set.");
+ * }
+ * @endcode
+ */
+const CCNxName *cpiRouteEntry_GetPrefix(const CPIRouteEntry *route);
+
+/**
+ * Get the interface index in the given `CPIRouteEntry`
+ *
+ * @param [in] route A pointer to a `CPIRouteEntry` instance.
+ *
+ * @return The interface index in the given `CPIRouteEntry`
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval *lifetimePtr = NULL;
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetimePtr, cost);
+ *
+ * assertTrue(cpiRouteEntry_GetInterfaceIndex(route), ifidx, "Route interface should be equal to what was set.");
+ * }
+ * @endcode
+ */
+unsigned cpiRouteEntry_GetInterfaceIndex(const CPIRouteEntry *route);
+
+/**
+ * Get the CPIAddress of the next hop for the given CPIRouteEntry instance.
+ *
+ * The nexthop may be used for certain types of routes to override the destination address. This might be used
+ * with some Ethernet routes, but is not necessary if one creates a cpiConnectionEthernet to the specific destination.
+ *
+ * @param [in] route A pointer to a `CPIRouteEntry` instance.
+ *
+ * @return non-null A pointer to a valid CPIAddress.
+ * @return null No nexthop was specified when the route was created.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = NULL;
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * assertNull(cpiRouteEntry_GetNexthop(route), "Route has no nexthop");
+ * }
+ * @endcode
+ */
+const CPIAddress *cpiRouteEntry_GetNexthop(const CPIRouteEntry *route);
+
+/**
+ * Determines if the Route Entry has a lifetime
+ *
+ * Returns true if a lifetime is specified for the route
+ *
+ * @param [in] route A non-null pointer to a CPIRouteEntry instance.
+ *
+ * @return true if the given CPIRouteEntry has a lifetime.
+ * @return false if it does not have a lifetime
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval *lifetimePtr = NULL;
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, lifetimePtr, cost);
+ *
+ * assertFalse(cpiRouteEntry_HasLifetime(route), "NULL lifetimePtr should return false");
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_GetLifetime()
+ */
+bool cpiRouteEntry_HasLifetime(const CPIRouteEntry *route);
+
+/**
+ * Returns the lifetime associated with a route
+ *
+ * The return value is undefined is the route does not have a lifetime. See cpiRouteEntry_HasLifetime().
+ *
+ * @param [in] route A non-null pointer to a CPIRouteEntry instance.
+ *
+ * @return The route lifetime
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ * struct timeval testLifetime = cpiRouteEntry_GetLifetime(route);
+ * assertTrue(timercmp(&lifetime, &testLifetime, ==), "Lifetimes should be equal");
+ * }
+ * @endcode
+ */
+struct timeval cpiRouteEntry_GetLifetime(const CPIRouteEntry *route);
+
+/**
+ * Returns the protocol identifer that created the route
+ *
+ * The ProtocolType identifies who created the route, such as a static route (administratively created) or
+ * a routing protocol such as ACRON.
+ *
+ * @param [in] route A non-null pointer to a CPIRouteEntry instance.
+ *
+ * @return `CPINameRouteProtocolType` the protocol specified when the route was created
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * CPINameRouteProtocolType protocol = cpiRouteEntry_GetRouteProtocolType(route);
+ * assertTrue(protocol == cpiNameRouteProtocolType_STATIC, "Route is of STATIC protocol");
+ * }
+ * @endcode
+ */
+CPINameRouteProtocolType cpiRouteEntry_GetRouteProtocolType(const CPIRouteEntry *route);
+
+/**
+ * Returns the type of route
+ *
+ * The route type determines how an Interest name matches the route.
+ *
+ * @param [in] route An allocated route entry
+ *
+ * @return `CPINameRouteType` The route type specified when the route entry was created
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * CPINameRouteType type = cpiRouteEntry_GetRouteType(route);
+ * assertTrue(type == cpiNameRouteType_LONGEST_MATCH, "Route is of LONGEST_MATCH type");
+ * }
+ * @endcode
+ */
+CPINameRouteType cpiRouteEntry_GetRouteType(const CPIRouteEntry *route);
+
+/**
+ * Get the "cost" value of the given `CPIRouteEntry`
+ *
+ * The cost may be used by some forwarding strategies to pick between alternatives.
+ *
+ * @param [in] route A pointer to a valid `CPIRouteEntry`
+ *
+ * @return The "cost" value of the given `CPIRouteEntry`
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName * prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress * nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * unsigned testCost = cpiRouteEntry_GetCost(route);
+ * assertTrue(cost == testCost, "Route cost should be 200");
+ * }
+ * @endcode
+ */
+unsigned cpiRouteEntry_GetCost(const CPIRouteEntry *route);
+
+/**
+ * Create a `PARCJSON` representation of the given `CPIRouteEntry` instance.
+ *
+ * @param [in] route A pointer to a valid `PARCJSON` instance.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to an allocated `CPIRouteEntry` instance that must be deallocated via `cpiRouteEntry_Destroy`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * PARCJSON *json = cpiRouteEntry_ToJson(route);
+ *
+ * ...
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_FromJson
+ */
+PARCJSON *cpiRouteEntry_ToJson(const CPIRouteEntry *route);
+
+/**
+ * Create a new `CPIRouteEntry` instance from the given `PARCJSON` instance.
+ *
+ * @param [in] json A pointer to a valid `PARCJSON` instance.
+ *
+ * @return NULL Memory could not be allocated.
+ * @return non-NULL A pointer to an allocated `CPIRouteEntry` instance that must be deallocated via `cpiRouteEntry_Destroy`.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * PARCJSON *json = cpiRouteEntry_ToJson(route);
+ *
+ * ...
+ *
+ * CPIRouteEntry *newRoute = cpiRouteEntry_FromJson(json);
+ * // route and newRoute will be equal
+ * }
+ * @endcode
+ *
+ * @see cpiRouteEntry_ToJson
+ */
+CPIRouteEntry *cpiRouteEntry_FromJson(PARCJSON *json);
+
+/**
+ * Produce a nul-terminated string representation of the specified instance.
+ *
+ * The result must be freed by the caller via {@link parcMemory_Deallocate}.
+ *
+ * @param [in] buffer A pointer to the instance.
+ *
+ * @return NULL Cannot allocate memory.
+ * @return non-NULL A pointer to an allocated, nul-terminated C string that must be deallocated via {@link parcMemory_Deallocate}.
+ *
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ * unsigned ifidx = 55;
+ * CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ * struct timeval lifetime = { 3600, 0 };
+ * unsigned cost = 200;
+ *
+ * CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ *
+ * printf("Route: %s\n", cpiRouteEntry_ToString(route));
+ * }
+ * @endcode
+ */
+char *cpiRouteEntry_ToString(CPIRouteEntry *route);
+
+/**
+ * Returns the symblic name associated with the route entry, may be NULL
+ *
+ * A symbolic name is not always associated with a route entry.
+ *
+ * @param [in] route An allocted CPIRouteEntry
+ *
+ * @return non-null The symbolic name associated with the route entry
+ * @return null No symbolic name is associated with the route entry.
+ *
+ * Example:
+ * @code
+ * {
+ * <#example#>
+ * }
+ * @endcode
+ */
+const char *cpiRouteEntry_GetSymbolicName(const CPIRouteEntry *route);
+
+#endif // libccnx_cpi_RouteEntry_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.c b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.c
new file mode 100644
index 00000000..c13b3f0d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.c
@@ -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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_ArrayList.h>
+
+#include <ccnx/api/control/cpi_RouteEntryList.h>
+#include <LongBow/runtime.h>
+
+struct cpi_route_entry_list {
+ PARCArrayList *listOfRouteEntries;
+};
+
+/**
+ * PARCArrayList entry destroyer
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+static void
+_cpiRouteEntryList_ArrayDestroyer(void **voidPtr)
+{
+ CPIRouteEntry **entryPtr = (CPIRouteEntry **) voidPtr;
+ cpiRouteEntry_Destroy(entryPtr);
+}
+
+CPIRouteEntryList *
+cpiRouteEntryList_Create()
+{
+ CPIRouteEntryList *list = parcMemory_AllocateAndClear(sizeof(CPIRouteEntryList));
+ assertNotNull(list, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(CPIRouteEntryList));
+ list->listOfRouteEntries = parcArrayList_Create(_cpiRouteEntryList_ArrayDestroyer);
+ return list;
+}
+
+void
+cpiRouteEntryList_Destroy(CPIRouteEntryList **listPtr)
+{
+ assertNotNull(listPtr, "Parameter must be non-null double pointer");
+ assertNotNull(*listPtr, "Parameter must dereference to non-null pointer");
+ CPIRouteEntryList *list = *listPtr;
+ parcArrayList_Destroy(&list->listOfRouteEntries);
+ parcMemory_Deallocate((void **) &list);
+ *listPtr = NULL;
+}
+
+void
+cpiRouteEntryList_Append(CPIRouteEntryList *list, CPIRouteEntry *entry)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ assertNotNull(entry, "Parameter entry must be non-null");
+
+ parcArrayList_Add(list->listOfRouteEntries, (PARCObject *) entry);
+}
+
+size_t
+cpiRouteEntryList_Length(const CPIRouteEntryList *list)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ return parcArrayList_Size(list->listOfRouteEntries);
+}
+
+CPIRouteEntry *
+cpiRouteEntryList_Get(CPIRouteEntryList *list, size_t index)
+{
+ assertNotNull(list, "Parameter list must be non-null");
+ CPIRouteEntry *original = (CPIRouteEntry *) parcArrayList_Get(list->listOfRouteEntries, index);
+ return cpiRouteEntry_Copy(original);
+}
+
+
+bool
+cpiRouteEntryList_Equals(const CPIRouteEntryList *a, const CPIRouteEntryList *b)
+{
+ if (a == NULL && b == NULL) {
+ return true;
+ }
+ if (a == NULL || b == NULL) {
+ return false;
+ }
+
+ if (parcArrayList_Size(a->listOfRouteEntries) == parcArrayList_Size(b->listOfRouteEntries)) {
+ size_t length = parcArrayList_Size(a->listOfRouteEntries);
+ for (size_t i = 0; i < length; i++) {
+ CPIRouteEntry *route_a = (CPIRouteEntry *) parcArrayList_Get(a->listOfRouteEntries, i);
+ CPIRouteEntry *route_b = (CPIRouteEntry *) parcArrayList_Get(b->listOfRouteEntries, i);
+ if (!cpiRouteEntry_Equals(route_a, route_b)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+const char cpi_RouteEntryList[] = "Routes";
+
+PARCJSON *
+cpiRouteEntryList_ToJson(const CPIRouteEntryList *list)
+{
+ assertNotNull(list, "Parameter must be non-null");
+
+ PARCJSONArray *routeList = parcJSONArray_Create();
+
+ size_t length = parcArrayList_Size(list->listOfRouteEntries);
+ for (size_t i = 0; i < length; i++) {
+ CPIRouteEntry *route = (CPIRouteEntry *) parcArrayList_Get(list->listOfRouteEntries, i);
+ PARCJSON *json = cpiRouteEntry_ToJson(route);
+ PARCJSONValue *value = parcJSONValue_CreateFromJSON(json);
+ parcJSON_Release(&json);
+ parcJSONArray_AddValue(routeList, value);
+ parcJSONValue_Release(&value);
+ }
+
+ PARCJSON *result = parcJSON_Create();
+ parcJSON_AddArray(result, cpi_RouteEntryList, routeList);
+ parcJSONArray_Release(&routeList);
+
+ return result;
+}
+
+CPIRouteEntryList *
+cpiRouteEntryList_FromJson(PARCJSON *json)
+{
+ assertNotNull(json, "Parameter must be non-null");
+
+ PARCJSONValue *value = parcJSON_GetValueByName(json, cpi_RouteEntryList);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpi_RouteEntryList,
+ parcJSON_ToString(json));
+ PARCJSONArray *routeList = parcJSONValue_GetArray(value);
+
+ CPIRouteEntryList *list = cpiRouteEntryList_Create();
+
+ size_t length = parcJSONArray_GetLength(routeList);
+ for (size_t i = 0; i < length; i++) {
+ PARCJSONValue *value = parcJSONArray_GetValue(routeList, i);
+ PARCJSON *routeJson = parcJSONValue_GetJSON(value);
+ CPIRouteEntry *route = cpiRouteEntry_FromJson(routeJson);
+ cpiRouteEntryList_Append(list, route);
+ }
+
+ return list;
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.h b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.h
new file mode 100644
index 00000000..049cdc97
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_RouteEntryList.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_RouteEntryList.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+#ifndef libccnx_cpi_RouteEntryList_h
+#define libccnx_cpi_RouteEntryList_h
+
+struct cpi_route_entry_list;
+typedef struct cpi_route_entry_list CPIRouteEntryList;
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+
+CPIRouteEntryList *cpiRouteEntryList_Create();
+void cpiRouteEntryList_Destroy(CPIRouteEntryList **listPtr);
+
+/**
+ * Adds a route entry to the list.
+ *
+ * Appends <code>entry</code> to the list. Takes ownership of the entry
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+void cpiRouteEntryList_Append(CPIRouteEntryList *list, CPIRouteEntry *entry);
+
+/**
+ * Determine if two CPIRouteEntryList instances are equal.
+ *
+ * Two CPIRouteEntryList instances are equal if, and only if,
+ * * ...
+ *
+ * The following equivalence relations on non-null `CPIRouteEntryList` instances are maintained:
+ *
+ * * It is reflexive: for any non-null reference value x, `CPIRouteEntryList_Equals(x, x)`
+ * must return true.
+ *
+ * * It is symmetric: for any non-null reference values x and y,
+ * `cpiRouteEntryList_Equals(x, y)` must return true if and only if
+ * `cpiRouteEntryList_Equals(y, x)` returns true.
+ *
+ * * It is transitive: for any non-null reference values x, y, and z, if
+ * `cpiRouteEntryList_Equals(x, y)` returns true and
+ * `cpiRouteEntryList_Equals(y, z)` returns true,
+ * then `cpiRouteEntryList_Equals(x, z)` must return true.
+ *
+ * * It is consistent: for any non-null reference values x and y, multiple
+ * invocations of `cpiRouteEntryList_Equals(x, y)` consistently return true or
+ * consistently return false.
+ *
+ * * For any non-null reference value x, `cpiRouteEntryList_Equals(x, NULL)` must
+ * return false.
+ *
+ * @param a A pointer to a `CPIRouteEntryList` instance.
+ * @param b A pointer to a `CPIRouteEntryList` instance.
+ * @return true if the two `CPIRouteEntryList` instances are equal.
+ *
+ * Example:
+ * @code
+ * {
+ * CPIRouteEntryList *a = cpiRouteEntryList_Create();
+ * CPIRouteEntryList *b = cpiRouteEntryList_Create();
+ *
+ * if (cpiRouteEntryList_Equals(a, b)) {
+ * // true
+ * } else {
+ * // false
+ * }
+ * }
+ * @endcode
+ */
+bool cpiRouteEntryList_Equals(const CPIRouteEntryList *a, const CPIRouteEntryList *b);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+size_t cpiRouteEntryList_Length(const CPIRouteEntryList *list);
+
+/**
+ * Returns a reference counted copy of the route entry.
+ *
+ * Caller must destroy the returned value.
+ * Will assert if you go beyond the end of the list.
+ *
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+CPIRouteEntry *cpiRouteEntryList_Get(CPIRouteEntryList *list, size_t index);
+
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+PARCJSON *cpiRouteEntryList_ToJson(const CPIRouteEntryList *list);
+
+/**
+ * <#One Line Description#>
+ *
+ * <#Paragraphs Of Explanation#>
+ *
+ * @param [<#in out in,out#>] <#name#> <#description#>
+ *
+ * @return <#value#> <#explanation#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ *
+ * @see <#references#>
+ */
+CPIRouteEntryList *cpiRouteEntryList_FromJson(PARCJSON *json);
+#endif // libccnx_cpi_RouteEntryList_h
diff --git a/libccnx-transport-rta/ccnx/api/control/cpi_private.h b/libccnx-transport-rta/ccnx/api/control/cpi_private.h
new file mode 100644
index 00000000..e13d347d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/cpi_private.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 cpi_private.h
+ * @brief <#Brief Description#>
+ *
+ * <#Detailed Description#>
+ *
+ */
+
+#ifndef libccnx_cpi_private_h
+#define libccnx_cpi_private_h
+
+uint64_t cpi_GetNextSequenceNumber(void);
+
+/**
+ * Wrap the operation in a CPI_REQUEST and add sequence number
+ *
+ * <#Discussion#>
+ *
+ * @param <#param1#>
+ * @return JSON "{ CPI_REQUEST: { SEQUENCE:number key: { operation } }}"
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSON *cpi_CreateRequest(const char *key, PARCJSON *operation);
+
+/**
+ * <#OneLineDescription#>
+ *
+ * INPUT: "{ CPI_REQUEST: { SEQUENCE:number key: { operation } }}"
+ * OUTPUT: "{ key : { operation } }"
+ *
+ * Example: "{ REGISTER : { <code>cpiRoute_ToJson()</code> } }"
+ * @param <#param1#>
+ * @return <#return#>
+ *
+ * Example:
+ * @code
+ * <#example#>
+ * @endcode
+ */
+PARCJSONPair *cpi_ParseRequest(PARCJSON *request);
+#endif // libccnx_cpi_private_h
diff --git a/libccnx-transport-rta/ccnx/api/control/test/.gitignore b/libccnx-transport-rta/ccnx/api/control/test/.gitignore
new file mode 100644
index 00000000..f6caadab
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/.gitignore
@@ -0,0 +1,24 @@
+test_controlPlaneInterface
+test_cpi_Acks
+test_cpi_Address
+test_cpi_AddressList
+test_cpi_CancelFlow
+test_cpi_Connection
+test_cpi_ControlFacade
+test_cpi_ControlMessage
+test_cpi_ConnectionEthernet
+test_cpi_ConnectionList
+test_cpi_Forwarding
+test_cpi_Interface
+test_cpi_InterfaceEthernet
+test_cpi_InterfaceGeneric
+test_cpi_InterfaceIPTunnel
+test_cpi_InterfaceIPTunnelList
+test_cpi_InterfaceSet
+test_cpi_InterfaceTypes
+test_cpi_Listener
+test_cpi_ManageLinks
+test_cpi_NameRouteType
+test_cpi_Registration
+test_cpi_RouteEntry
+test_cpi_RouteEntryList
diff --git a/libccnx-transport-rta/ccnx/api/control/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/api/control/test/CMakeLists.txt
new file mode 100644
index 00000000..a573213c
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/CMakeLists.txt
@@ -0,0 +1,36 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_cpi_Acks
+ test_cpi_Address
+ test_cpi_AddressList
+ test_cpi_CancelFlow
+ test_cpi_Connection
+ test_cpi_ConnectionEthernet
+ test_cpi_ConnectionList
+ test_cpi_ControlMessage
+ test_cpi_ControlFacade
+ test_cpi_Interface
+ test_cpi_InterfaceSet
+ test_cpi_InterfaceTypes
+ test_cpi_InterfaceGeneric
+ test_cpi_InterfaceEthernet
+ test_cpi_InterfaceIPTunnel
+ test_cpi_InterfaceIPTunnelList
+ test_cpi_Forwarding
+ test_cpi_Listener
+ test_cpi_ManageLinks
+ test_cpi_NameRouteType
+ test_cpi_Registration
+ test_cpi_RouteEntry
+ test_cpi_RouteEntryList
+ test_controlPlaneInterface
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_controlPlaneInterface.c b/libccnx-transport-rta/ccnx/api/control/test/test_controlPlaneInterface.c
new file mode 100644
index 00000000..8dfbb362
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_controlPlaneInterface.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../controlPlaneInterface.c"
+
+#include <LongBow/unit-test.h>
+
+#include <ccnx/api/control/cpi_ControlFacade.h>
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(controlPlaneInterface)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(controlPlaneInterface)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(controlPlaneInterface)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpi_CreateRequest);
+ LONGBOW_RUN_TEST_CASE(Global, cpi_CreateResponse);
+ LONGBOW_RUN_TEST_CASE(Global, cpi_ParseRequest);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpi_PauseInput);
+
+ LONGBOW_RUN_TEST_CASE(Global, controlPlaneInterface_PauseInput);
+}
+
+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, cpi_CreateRequest)
+{
+ const char key_looney[] = "looney";
+
+ PARCJSON *operation = parcJSON_Create();
+ parcJSON_AddString(operation, "bugs", "bunny");
+
+ PARCJSON *request = cpi_CreateRequest(key_looney, operation);
+ parcJSON_Release(&operation);
+
+ PARCJSONValue *value = parcJSON_GetValueByName(request, cpiRequest);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiRequest,
+ parcJSON_ToString(request));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Key %s wrong type: %s",
+ cpiRequest,
+ parcJSON_ToString(request));
+ PARCJSON *test_request_key = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(test_request_key, cpiSeqnum);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiSeqnum,
+ parcJSON_ToString(request));
+ assertTrue(parcJSONValue_IsNumber(value),
+ "Key %s wrong type: %s",
+ cpiSeqnum,
+ parcJSON_ToString(request));
+
+ value = parcJSON_GetValueByName(test_request_key, key_looney);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ key_looney,
+ parcJSON_ToString(request));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Key %s wrong type: %s",
+ key_looney,
+ parcJSON_ToString(request));
+
+ parcJSON_Release(&request);
+}
+
+LONGBOW_TEST_CASE(Global, cpi_CreateResponse)
+{
+ const char key_looney[] = "looney";
+
+ PARCJSON *requestOperation = parcJSON_Create();
+ parcJSON_AddString(requestOperation, "bugs", "bunny");
+
+ PARCJSON *requestJson = cpi_CreateRequest(key_looney, requestOperation);
+ parcJSON_Release(&requestOperation);
+
+ //
+ // we should now have an object that looks, like
+ // { "CPI_REQUEST": { "SEQUENCE":1, "looney":{"bugs":"bunny"} } }
+ //
+ // The sequence number might not be exactly 1, depending on how the
+ // unit tests are ordered and forked.
+ //
+ char *expectedStr1 = "{\"CPI_REQUEST\":{\"SEQUENCE\":";
+ char *expectedStr2 = ",\"looney\":{\"bugs\":\"bunny\"}}}";
+ char *jsonStr = parcJSON_ToCompactString(requestJson);
+ size_t len = strlen(expectedStr1);
+ assertTrue(strncmp(jsonStr, expectedStr1, len) == 0, "Expect JSON strings to be the same");
+ assertTrue(strncmp(jsonStr + len + 1, expectedStr2, strlen(expectedStr2)) == 0, "Expect JSON strings to be the same");
+ parcMemory_Deallocate(&jsonStr);
+
+ CCNxControl *request = ccnxControl_CreateCPIRequest(requestJson);
+
+ PARCJSON *responseOperation = parcJSON_Create();
+ parcJSON_AddString(responseOperation, "donald", "duck");
+
+ CCNxControl *response = cpi_CreateResponse(request, responseOperation);
+ parcJSON_Release(&responseOperation);
+
+ PARCJSON *responseJson = ccnxControl_GetJson(response);
+ //
+ // we should now have an object that looks, like
+ // { "CPI_RESPONSE": { "SEQUENCE":1, "looney":{"donald":"duck"} } }
+ //
+ // The sequence number might not be exactly 1, depending on how the
+ // unit tests are ordered and forked. It should be the same as the request.
+ //
+ expectedStr1 = "{\"CPI_RESPONSE\":{\"SEQUENCE\":";
+ expectedStr2 = ",\"looney\":{\"donald\":\"duck\"}}}";
+ jsonStr = parcJSON_ToCompactString(responseJson);
+ len = strlen(expectedStr1);
+ assertTrue(strncmp(jsonStr, expectedStr1, len) == 0, "Expect JSON strings to be the same");
+ assertTrue(strncmp(jsonStr + len + 1, expectedStr2, strlen(expectedStr2)) == 0, "Expect JSON strings to be the same");
+ parcMemory_Deallocate(&jsonStr);
+
+ PARCJSONValue *value = parcJSON_GetValueByName(responseJson, cpiResponse);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiResponse,
+ parcJSON_ToString(responseJson));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Key %s wrong type: %s",
+ cpiResponse,
+ parcJSON_ToString(responseJson));
+ PARCJSON *test_response_key = parcJSONValue_GetJSON(value);
+
+ value = parcJSON_GetValueByName(test_response_key, cpiSeqnum);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ cpiSeqnum,
+ parcJSON_ToString(test_response_key));
+ assertTrue(parcJSONValue_IsNumber(value),
+ "Key %s wrong type: %s",
+ cpiSeqnum,
+ parcJSON_ToString(responseJson));
+
+ value = parcJSON_GetValueByName(test_response_key, key_looney);
+ assertNotNull(value,
+ "JSON key not found %s: %s",
+ key_looney,
+ parcJSON_ToString(responseJson));
+ assertTrue(parcJSONValue_IsJSON(value),
+ "Key %s wrong type: %s",
+ key_looney,
+ parcJSON_ToString(responseJson));
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+
+ parcJSON_Release(&requestJson);
+}
+
+LONGBOW_TEST_CASE(Global, cpi_ParseRequest)
+{
+ char key_looney[] = "looney";
+ const char value_looney[] = "{\"bugs\":\"bunny\"}";
+
+ PARCJSON *operation = parcJSON_Create();
+ parcJSON_AddString(operation, "bugs", "bunny");
+
+ PARCJSON *request = cpi_CreateRequest(key_looney, operation);
+ parcJSON_Release(&operation);
+
+ //
+ // we should now have an object that looks, like
+ // { "CPI_REQUEST": { "SEQUENCE":1, "looney":{"bugs":"bunny"} } }
+ //
+ // The sequence number might not be exactly 1, depending on how the
+ // unit tests are ordered and forked.
+ //
+
+ PARCJSONPair *parsedOpPair = cpi_ParseRequest(request);
+ assertNotNull(parsedOpPair, "Got null parsed json from %s", parcJSON_ToString(request));
+
+ PARCBuffer *key = parcJSONPair_GetName(parsedOpPair);
+ PARCBuffer *test_key = parcBuffer_WrapCString(key_looney);
+ assertTrue(parcBuffer_Equals(key, test_key),
+ "Key name of parsed wrong, expected %s got %s in %s",
+ key_looney,
+ parcBuffer_ToString(key),
+ parcJSONPair_ToString(parsedOpPair));
+ parcBuffer_Release(&test_key);
+
+ operation = parcJSONValue_GetJSON(parcJSONPair_GetValue(parsedOpPair));
+ char *test_looney = parcJSON_ToCompactString(operation);
+ assertTrue(strcmp(value_looney, test_looney) == 0,
+ "Inner values did not match, expected %s got %s in %s",
+ value_looney,
+ test_looney,
+ parcJSONPair_ToString(parsedOpPair));
+ parcMemory_Deallocate((void **) &test_looney);
+
+ parcJSON_Release(&request);
+}
+
+LONGBOW_TEST_CASE(Global, controlPlaneInterface_PauseInput)
+{
+ CCNxControl *message = ccnxControl_CreatePauseInputRequest();
+
+ assertTrue(ccnxControlFacade_IsCPI(message), "Expected ccnxControlMessage_IsCPI to be true");
+
+ PARCJSON *json = ccnxControlFacade_GetJson(message);
+ assertTrue(ccnxControl_IsCPI(message), "Expected a CPI control message");
+ assertTrue(cpi_getCPIOperation2(json) == CPI_PAUSE,
+ "Expected opertaion %d got %d", CPI_PAUSE, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&message);
+}
+
+LONGBOW_TEST_CASE(Global, cpi_PauseInput)
+{
+ PARCJSON *pauseRequest = cpi_CreatePauseInputRequest();
+ CCNxControl *request = ccnxControl_CreateCPIRequest(pauseRequest);
+ parcJSON_Release(&pauseRequest);
+
+ assertTrue(ccnxControl_IsCPI(request), "Is not a CPI message!");
+ assertTrue(cpi_GetMessageType(request) == CPI_REQUEST,
+ "Got wrong message type, expected %d got %d",
+ CPI_REQUEST,
+ cpi_GetMessageType(request));
+
+ assertTrue(cpi_GetMessageOperation(request) == CPI_PAUSE,
+ "got wrong operation, expected %d got %d",
+ CPI_PAUSE,
+ cpi_GetMessageOperation(request));
+
+ ccnxControl_Release(&request);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(controlPlaneInterface);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Acks.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Acks.c
new file mode 100644
index 00000000..ecdcf40f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Acks.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_Acks.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_Acks)
+{
+ // 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(cpi_Acks)
+{
+ 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(cpi_Acks)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiAck_CreateAck);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAck_CreateNack);
+}
+
+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, cpiAck_CreateAck)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ PARCJSON *request = cpiForwarding_CreateAddRouteRequest(route);
+
+ PARCJSON *actual = cpiAcks_CreateAck(request);
+
+ assertTrue(cpiAcks_IsAck(actual), "Expected cpiAcks_IsAck to return true.");
+
+ parcJSON_Release(&actual);
+ parcJSON_Release(&request);
+ cpiRouteEntry_Destroy(&route);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAck_CreateNack)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/foo/bar");
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ PARCJSON *request = cpiForwarding_CreateAddRouteRequest(route);
+
+ PARCJSON *actual = cpiAcks_CreateNack(request);
+
+ assertFalse(cpiAcks_IsAck(actual), "Expected cpiAcks_IsAck to return false.");
+
+ parcJSON_Release(&actual);
+ parcJSON_Release(&request);
+ cpiRouteEntry_Destroy(&route);
+ ccnxName_Release(&name);
+}
+
+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(cpi_Acks);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Address.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Address.c
new file mode 100644
index 00000000..fbb63f7d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Address.c
@@ -0,0 +1,550 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Each type test (inet, inet6, etc.) should test:
+ * - CreateFromX
+ * - GetX
+ * - GetType = X
+ * - Y = cpiAddress_CreateFromJson( cpiAddress_ToJson(X) ) == X
+ * - Equals(Y, X)
+ * - Equals(Copy(X), X)
+ */
+
+// for inet_pton
+#include <config.h>
+#include <arpa/inet.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../cpi_Address.c"
+
+LONGBOW_TEST_RUNNER(cpi_Address)
+{
+ // 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(cpi_Address)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_Address)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_Equals_ReallyEqual);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_Equals_SamePointer);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_Equals_NotEqual);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_CreateFromInet);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_CreateFromInet6);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_CreateFromInterface);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_CreateFromLink);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_CreateFromUnix);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_ToString_INET);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_ToString_INET6);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_ToString_LINK);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_ToString_IFACE);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_ToString_UNIX);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddress_BuildString);
+}
+
+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, cpiAddress_Copy)
+{
+ CPIAddress *a = cpiAddress_CreateFromInterface(1);
+ CPIAddress *b = cpiAddress_Copy(a);
+
+ assertTrue(cpiAddress_Equals(a, b), "Copy did not compare as equal: %s and %s", cpiAddress_ToString(a), cpiAddress_ToString(b));
+
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_Equals_ReallyEqual)
+{
+ struct sockaddr_in addr_in;
+ memset(&addr_in, 0, sizeof(struct sockaddr_in));
+
+ addr_in.sin_addr.s_addr = 0x01020304;
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = 0x0A0B;
+
+ CPIAddress *a = cpiAddress_CreateFromInet(&addr_in);
+ CPIAddress *b = cpiAddress_CreateFromInet(&addr_in);
+
+ assertTrue(cpiAddress_Equals(a, b), "Equals did not compare two equal addresses: %s and %s", cpiAddress_ToString(a), cpiAddress_ToString(b));
+
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_Equals_SamePointer)
+{
+ struct sockaddr_in addr_in;
+ memset(&addr_in, 0, sizeof(struct sockaddr_in));
+
+ addr_in.sin_addr.s_addr = 0x01020304;
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = 0x0A0B;
+
+ CPIAddress *a = cpiAddress_CreateFromInet(&addr_in);
+
+ assertTrue(cpiAddress_Equals(a, a), "Equals did not compare two equal addresses: %s and %s", cpiAddress_ToString(a), cpiAddress_ToString(a));
+
+ cpiAddress_Destroy(&a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_Equals_NotEqual)
+{
+ struct sockaddr_in addr_in;
+ memset(&addr_in, 0, sizeof(struct sockaddr_in));
+
+ addr_in.sin_addr.s_addr = 0x01020304;
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = 0x0A0B;
+
+ CPIAddress *a = cpiAddress_CreateFromInet(&addr_in);
+ CPIAddress *b = cpiAddress_CreateFromInterface(1);
+
+ assertFalse(cpiAddress_Equals(a, b), "Equals failed on different addresses: %s and %s", cpiAddress_ToString(a), cpiAddress_ToString(b));
+
+ cpiAddress_Destroy(&a);
+ cpiAddress_Destroy(&b);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiAddress_CreateFromInet)
+{
+ struct sockaddr_in addr_in;
+ struct sockaddr_in addr_test;
+ memset(&addr_in, 0, sizeof(struct sockaddr_in));
+
+ addr_in.sin_addr.s_addr = 0x01020304;
+ addr_in.sin_family = AF_INET;
+ addr_in.sin_port = 0x0A0B;
+
+ CPIAddress *address = cpiAddress_CreateFromInet(&addr_in);
+
+ bool success = cpiAddress_GetInet(address, &addr_test);
+ assertTrue(success, "Got false converting back address");
+
+ assertTrue(memcmp(&addr_in, &addr_test, sizeof(struct sockaddr_in)) == 0, "Got mismatch addressed");
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddressType_INET,
+ "Got wrong address type, expected %d, got %d", cpiAddressType_INET, cpiAddress_GetType(address));
+
+ PARCJSON *json = cpiAddress_ToJson(address);
+ CPIAddress *fromjson = cpiAddress_CreateFromJson(json);
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddress_GetType(fromjson), "fromjson type does not equal known");
+ assertTrue(parcBuffer_Equals(address->blob, fromjson->blob), "fromjson blob does not equal known address");
+ assertTrue(cpiAddress_Equals(address, fromjson), "cpiAddress_Equals broken for INET type");
+
+ // This test does too much. Case 1032
+ CPIAddress *copy = cpiAddress_Copy(address);
+ assertTrue(cpiAddress_Equals(copy, address), "Copy and address not equal for INET");
+
+ cpiAddress_Destroy(&copy);
+ cpiAddress_Destroy(&fromjson);
+
+ parcJSON_Release(&json);
+
+ cpiAddress_Destroy(&address);
+ return;
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_CreateFromInet6)
+{
+ struct sockaddr_in6 addr_in6;
+ memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+
+ inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ addr_in6.sin6_family = AF_INET6;
+ addr_in6.sin6_port = 0x0A0B;
+ addr_in6.sin6_flowinfo = 0x01020304;
+
+ CPIAddress *address = cpiAddress_CreateFromInet6(&addr_in6);
+
+ struct sockaddr_in6 addr_test;
+ bool success = cpiAddress_GetInet6(address, &addr_test);
+ assertTrue(success, "Got false converting back address");
+
+ assertTrue(memcmp(&addr_in6, &addr_test, sizeof(struct sockaddr_in6)) == 0, "Got mismatch addressed");
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddressType_INET6,
+ "Got wrong address type, expected %d, got %d", cpiAddressType_INET6, cpiAddress_GetType(address));
+
+ PARCJSON *json = cpiAddress_ToJson(address);
+ CPIAddress *fromjson = cpiAddress_CreateFromJson(json);
+
+ assertTrue(parcBuffer_Equals(address->blob, fromjson->blob), "fromjson blob does not equal known address");
+ assertTrue(cpiAddress_Equals(address, fromjson), "cpiAddress_Equals broken for INET6 type");
+
+ CPIAddress *copy = cpiAddress_Copy(address);
+ assertTrue(cpiAddress_Equals(copy, address), "Copy and address not equal for INET6");
+
+ parcJSON_Release(&json);
+ cpiAddress_Destroy(&address);
+ cpiAddress_Destroy(&copy);
+ cpiAddress_Destroy(&fromjson);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_CreateFromUnix)
+{
+ struct sockaddr_un addr_un;
+ struct sockaddr_un addr_test;
+ memset(&addr_un, 0, sizeof(struct sockaddr_un));
+ char path[] = "/Hello/Cruel/World";
+ strcpy(addr_un.sun_path, path);
+ addr_un.sun_family = AF_UNIX;
+
+ CPIAddress *address = cpiAddress_CreateFromUnix(&addr_un);
+
+ bool success = cpiAddress_GetUnix(address, &addr_test);
+ assertTrue(success, "Got false converting back address");
+
+ assertTrue(memcmp(&addr_un, &addr_test, sizeof(struct sockaddr_un)) == 0, "Got mismatch addressed");
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddressType_UNIX,
+ "Got wrong address type, expected %d, got %d", cpiAddressType_UNIX, cpiAddress_GetType(address));
+
+ PARCJSON *json = cpiAddress_ToJson(address);
+ CPIAddress *fromjson = cpiAddress_CreateFromJson(json);
+
+ assertTrue(parcBuffer_Equals(address->blob, fromjson->blob), "fromjson blob does not equal known address");
+ assertTrue(cpiAddress_Equals(address, fromjson), "cpiAddress_Equals broken for UNIX type");
+
+ CPIAddress *copy = cpiAddress_Copy(address);
+ assertTrue(cpiAddress_Equals(copy, address), "Copy and address not equal for UNIX");
+
+ parcJSON_Release(&json);
+ cpiAddress_Destroy(&address);
+ cpiAddress_Destroy(&copy);
+ cpiAddress_Destroy(&fromjson);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_CreateFromInterface)
+{
+ uint32_t ifidx = 0x01020304;
+ uint32_t test;
+
+ CPIAddress *address = cpiAddress_CreateFromInterface(ifidx);
+
+ bool success = cpiAddress_GetInterfaceIndex(address, &test);
+ assertTrue(success, "Got false converting back address");
+
+ assertTrue(ifidx == test, "Got mismatch addressed");
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddressType_IFACE,
+ "Got wrong address type, expected %d, got %d", cpiAddressType_IFACE, cpiAddress_GetType(address));
+
+ PARCJSON *json = cpiAddress_ToJson(address);
+ CPIAddress *fromjson = cpiAddress_CreateFromJson(json);
+
+ assertTrue(parcBuffer_Equals(address->blob, fromjson->blob), "fromjson blob does not equal known address");
+ assertTrue(cpiAddress_Equals(address, fromjson), "cpiAddress_Equals broken for IFACE type");
+
+ CPIAddress *copy = cpiAddress_Copy(address);
+ assertTrue(cpiAddress_Equals(copy, address), "Copy and address not equal for IFACE");
+
+ parcJSON_Release(&json);
+ cpiAddress_Destroy(&address);
+ cpiAddress_Destroy(&copy);
+ cpiAddress_Destroy(&fromjson);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_CreateFromLink)
+{
+ uint8_t mac[] = { 0x01, 0x02, 0x03, 0x04, 0xFF, 0x8F };
+ PARCBuffer *macbuffer = parcBuffer_Flip(parcBuffer_CreateFromArray(mac, sizeof(mac)));
+
+ CPIAddress *address = cpiAddress_CreateFromLink(mac, sizeof(mac));
+
+ // Do not release test, it is the same reference as address->blob
+ PARCBuffer *test = cpiAddress_GetLinkAddress(address);
+ assertNotNull(test, "Got null link address buffer");
+ assertTrue(parcBuffer_Equals(test, address->blob), "Returned buffer from cpiAddress_GetLinkAddress not equal to address");
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddressType_LINK,
+ "Got wrong address type, expected %d, got %d", cpiAddressType_LINK, cpiAddress_GetType(address));
+
+ PARCJSON *json = cpiAddress_ToJson(address);
+ CPIAddress *fromjson = cpiAddress_CreateFromJson(json);
+
+ assertTrue(cpiAddress_GetType(address) == cpiAddress_GetType(fromjson),
+ "fromjson type does not equal known");
+ assertTrue(parcBuffer_Equals(address->blob, fromjson->blob),
+ "fromjson blob does not equal known address");
+ assertTrue(cpiAddress_Equals(address, fromjson),
+ "cpiAddress_Equals broken for LINK type");
+
+ CPIAddress *copy = cpiAddress_Copy(address);
+ assertTrue(cpiAddress_Equals(copy, address),
+ "Copy and address not equal for LINK");
+
+ parcJSON_Release(&json);
+ cpiAddress_Destroy(&address);
+ cpiAddress_Destroy(&copy);
+ cpiAddress_Destroy(&fromjson);
+ parcBuffer_Release(&macbuffer);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_ToString_INET)
+{
+ struct sockaddr_in *addr_in = parcNetwork_SockInet4Address("1.2.3.4", 12345);
+
+ char expected[] = "inet4://1.2.3.4:12345";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInet(addr_in);
+
+ char *actual = cpiAddress_ToString(cpiaddr);
+
+ assertTrue(strcmp(actual, expected) == 0, "Bad string, expected '%s' got '%s'", expected, actual);
+
+ parcMemory_Deallocate((void **) &actual);
+ cpiAddress_Destroy(&cpiaddr);
+ parcMemory_Deallocate((void **) &addr_in);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_ToString_UNIX)
+{
+ struct sockaddr_un addr_un;
+ char path[] = "/Hello/Cruel/World";
+ memset(&addr_un, 0, sizeof(struct sockaddr_un));
+ strcpy(addr_un.sun_path, path);
+ addr_un.sun_family = AF_UNIX;
+
+ char truth_str[] = "{ .type=UNIX, .data={ .path=/Hello/Cruel/World, .len=18 } }";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromUnix(&addr_un);
+
+ char *output = cpiAddress_ToString(cpiaddr);
+
+ assertTrue(strcmp(output, truth_str) == 0, "Bad string, expected %s got %s", truth_str, output);
+
+ parcMemory_Deallocate((void **) &output);
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_ToString_INET6)
+{
+ struct sockaddr_in6 addr_in6;
+ memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+
+ inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ addr_in6.sin6_family = AF_INET6;
+ addr_in6.sin6_port = htons(43215);
+
+ char *expected = "inet6://[2001:720:1500:1::a100%0]:43215";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInet6(&addr_in6);
+ char *actual = cpiAddress_ToString(cpiaddr);
+
+ assertTrue(strcmp(expected, actual) == 0, "Expected '%s', actual '%s'", expected, actual);
+
+ parcMemory_Deallocate((void **) &actual);
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_ToString_LINK)
+{
+ uint8_t addr[6] = { 0x01, 0x02, 0x03, 0xF4, 0xF5, 0xF6 };
+
+ char truth_str[] = "link://01-02-03-f4-f5-f6";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromLink(addr, sizeof(addr));
+ char *output = cpiAddress_ToString(cpiaddr);
+
+ assertTrue(strcmp(output, truth_str) == 0,
+ "Bad string, expected %s got %s", truth_str, output);
+
+ parcMemory_Deallocate((void **) &output);
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_ToString_IFACE)
+{
+ char truth_str[] = "{ .type=IFACE, .data={ .ifidx=55 } }";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInterface(55);
+ char *output = cpiAddress_ToString(cpiaddr);
+
+ assertTrue(strcmp(output, truth_str) == 0, "Bad string, expected %s got %s", truth_str, output);
+
+ parcMemory_Deallocate((void **) &output);
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddress_BuildString)
+{
+ CPIAddress *address = cpiAddress_CreateFromInterface(1);
+ uint32_t beforeBalance = parcMemory_Outstanding();
+ PARCBufferComposer *composer = cpiAddress_BuildString(address, parcBufferComposer_Create());
+ parcBufferComposer_Release(&composer);
+ uint32_t afterBalance = parcMemory_Outstanding();
+
+ cpiAddress_Destroy(&address);
+ assertTrue(beforeBalance == afterBalance, "Memory leak off by %d allocations", (int) (afterBalance - beforeBalance));
+}
+
+// ===============================================================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _Inet_BuildString);
+ LONGBOW_RUN_TEST_CASE(Local, _Inet6_BuildString);
+ LONGBOW_RUN_TEST_CASE(Local, _LinkToString);
+ LONGBOW_RUN_TEST_CASE(Local, _IfaceToString);
+}
+
+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;
+}
+
+LONGBOW_TEST_CASE(Local, _Inet_BuildString)
+{
+ struct sockaddr_in addr_in;
+ addr_in.sin_addr.s_addr = 0x04030201;
+ addr_in.sin_port = htons(12345);
+
+ char *expected = "inet4://1.2.3.4:12345";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInet(&addr_in);
+
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ _Inet_BuildString(cpiaddr, composer);
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *actual = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+
+ assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual);
+ parcMemory_Deallocate((void **) &actual);
+
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Local, _Inet6_BuildString)
+{
+ struct sockaddr_in6 addr_in6;
+ memset(&addr_in6, 0, sizeof(struct sockaddr_in6));
+
+ inet_pton(AF_INET6, "2001:720:1500:1::a100", &(addr_in6.sin6_addr));
+ addr_in6.sin6_family = AF_INET6;
+ addr_in6.sin6_port = htons(43215);
+
+ char *expected = "inet6://[2001:720:1500:1::a100%0]:43215";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInet6(&addr_in6);
+
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ _Inet6_BuildString(cpiaddr, composer);
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *actual = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+
+ assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual);
+ parcMemory_Deallocate((void **) &actual);
+
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Local, _LinkToString)
+{
+ uint8_t addr[6] = { 0x01, 0x02, 0x03, 0xF4, 0xF5, 0xF6 };
+
+ char *expected = "link://01-02-03-f4-f5-f6";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromLink(addr, sizeof(addr));
+
+ PARCBufferComposer *composer = parcBufferComposer_Create();
+ _Link_BuildString(cpiaddr, composer);
+
+ PARCBuffer *tempBuffer = parcBufferComposer_ProduceBuffer(composer);
+ char *actual = parcBuffer_ToString(tempBuffer);
+ parcBuffer_Release(&tempBuffer);
+ parcBufferComposer_Release(&composer);
+
+ assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual);
+ parcMemory_Deallocate((void **) &actual);
+
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+LONGBOW_TEST_CASE(Local, _IfaceToString)
+{
+ char truth_str[] = "{ .ifidx=55 }";
+
+ CPIAddress *cpiaddr = cpiAddress_CreateFromInterface(55);
+
+ char output[1024];
+ ssize_t output_length = _IfaceToString(output, 1024, cpiaddr->blob);
+ assertTrue(strcmp(output, truth_str) == 0, "Bad string, expected %s got %s", truth_str, output);
+ assertTrue(strlen(truth_str) == output_length, "Got wrong output size, expected %zd got %zd", strlen(truth_str), output_length);
+
+ cpiAddress_Destroy(&cpiaddr);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_Address);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_AddressList.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_AddressList.c
new file mode 100644
index 00000000..f4887a45
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_AddressList.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_AddressList.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_AddressList)
+{
+ // 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(cpi_AddressList)
+{
+ 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(cpi_AddressList)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Append);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_GetItem);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Equals_same_pointer);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Equals_both_empty);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Equals_unequal_sizes);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Equals_same_lists);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_Equals_wrong_order);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_FromJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_ToFromJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiAddressList_ToString);
+}
+
+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, cpiAddressList_Append)
+{
+ CPIAddressList *list = cpiAddressList_Create();
+ unsigned loops = 10;
+
+ for (unsigned i = 0; i < loops; i++) {
+ cpiAddressList_Append(list, cpiAddress_CreateFromInterface(i));
+ }
+
+ assertTrue(cpiAddressList_Length(list) == loops,
+ "Got wrong length, expected %u got %zu",
+ loops,
+ cpiAddressList_Length(list));
+
+ cpiAddressList_Destroy(&list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Copy)
+{
+ CPIAddressList *list = cpiAddressList_Create();
+ unsigned loops = 10;
+
+ for (unsigned i = 0; i < loops; i++) {
+ cpiAddressList_Append(list, cpiAddress_CreateFromInterface(i));
+ }
+
+ CPIAddressList *copy = cpiAddressList_Copy(list);
+ assertTrue(cpiAddressList_Length(copy) == cpiAddressList_Length(list),
+ "Copy wrong size, got %zu expected %zu",
+ cpiAddressList_Length(copy),
+ cpiAddressList_Length(list));
+
+ for (unsigned i = 0; i < cpiAddressList_Length(copy); i++) {
+ const CPIAddress *truth = cpiAddressList_GetItem(list, i);
+ const CPIAddress *test = cpiAddressList_GetItem(copy, i);
+ assertTrue(cpiAddress_Equals(truth, test), "Lists do not match at element %u", i);
+ }
+
+ cpiAddressList_Destroy(&list);
+ cpiAddressList_Destroy(&copy);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Create_Destroy)
+{
+ CPIAddressList *list = cpiAddressList_Create();
+ cpiAddressList_Destroy(&list);
+ assertTrue(parcMemory_Outstanding() == 0, "Got memory imbalance: %u", parcMemory_Outstanding());
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_GetItem)
+{
+ CPIAddressList *list = cpiAddressList_Create();
+ unsigned loops = 10;
+
+ for (unsigned i = 0; i < loops; i++) {
+ cpiAddressList_Append(list, cpiAddress_CreateFromInterface(i));
+ }
+
+ assertTrue(cpiAddressList_Length(list) == loops,
+ "Got wrong length, expected %u got %zu",
+ loops,
+ cpiAddressList_Length(list));
+
+ CPIAddress *truth = cpiAddress_CreateFromInterface(5);
+ const CPIAddress *test = cpiAddressList_GetItem(list, 5);
+ assertTrue(cpiAddress_Equals(truth, test), "Item 5 did not match!");
+
+ cpiAddressList_Destroy(&list);
+ cpiAddress_Destroy(&truth);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Equals_same_pointer)
+{
+ CPIAddressList *list = cpiAddressList_Create();
+ assertTrue(cpiAddressList_Equals(list, list), "list != list, that's wrong");
+ cpiAddressList_Destroy(&list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Equals_both_empty)
+{
+ CPIAddressList *a = cpiAddressList_Create();
+ CPIAddressList *b = cpiAddressList_Create();
+ assertTrue(cpiAddressList_Equals(a, b), "emtpy list != empty list, that's wrong");
+ cpiAddressList_Destroy(&a);
+ cpiAddressList_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Equals_unequal_sizes)
+{
+ CPIAddressList *a = cpiAddressList_Create();
+ CPIAddressList *b = cpiAddressList_Create();
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(1));
+ assertFalse(cpiAddressList_Equals(a, b), "length 0 == length 1, that's wrong");
+ cpiAddressList_Destroy(&a);
+ cpiAddressList_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Equals_same_lists)
+{
+ CPIAddressList *a = cpiAddressList_Create();
+ CPIAddressList *b = cpiAddressList_Create();
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(1));
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(2));
+ cpiAddressList_Append(b, cpiAddress_CreateFromInterface(1));
+ cpiAddressList_Append(b, cpiAddress_CreateFromInterface(2));
+ assertTrue(cpiAddressList_Equals(a, b), "same lists not equal, that's wrong");
+ cpiAddressList_Destroy(&a);
+ cpiAddressList_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_Equals_wrong_order)
+{
+ CPIAddressList *a = cpiAddressList_Create();
+ CPIAddressList *b = cpiAddressList_Create();
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(1));
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(2));
+ cpiAddressList_Append(b, cpiAddress_CreateFromInterface(2));
+ cpiAddressList_Append(b, cpiAddress_CreateFromInterface(1));
+ assertFalse(cpiAddressList_Equals(a, b), "out of order lists equal, that's wrong");
+ cpiAddressList_Destroy(&a);
+ cpiAddressList_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_ToJSON)
+{
+ char truth[] = "[{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAA==\"},{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAQ==\"}]";
+
+ CPIAddressList *a = cpiAddressList_Create();
+ int loops = 2;
+ for (int i = 0; i < loops; i++) {
+ cpiAddressList_Append(a, cpiAddress_CreateFromInterface(i));
+ }
+
+ PARCJSONArray *jsonArray = cpiAddressList_ToJson(a);
+ char *test = parcJSONArray_ToCompactString(jsonArray);
+
+ assertTrue(strcmp(truth, test) == 0, "JSON strings did not match, got '%s' expected '%s'", test, truth);
+
+ cpiAddressList_Destroy(&a);
+ parcMemory_Deallocate((void **) &test);
+ parcJSONArray_Release(&jsonArray);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_FromJSON)
+{
+ char json_str[] = "{\"ARRAY\":[{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAA==\"},{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAQ==\"}]}";
+ PARCJSON *json = parcJSON_ParseString(json_str);
+
+ PARCJSONArray *jsonArray = parcJSONValue_GetArray(parcJSON_GetValueByIndex(json, 0));
+
+ CPIAddressList *test_list = cpiAddressList_CreateFromJson(jsonArray);
+
+ CPIAddressList *truth_list = cpiAddressList_Create();
+ int loops = 2;
+ for (int i = 0; i < loops; i++) {
+ cpiAddressList_Append(truth_list, cpiAddress_CreateFromInterface(i));
+ }
+
+ assertTrue(cpiAddressList_Equals(truth_list, test_list), "Lists did not match!");
+
+ cpiAddressList_Destroy(&truth_list);
+ cpiAddressList_Destroy(&test_list);
+ parcJSON_Release(&json);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_ToFromJSON)
+{
+ CPIAddressList *truth_list = cpiAddressList_Create();
+ int loops = 2;
+ for (int i = 0; i < loops; i++) {
+ cpiAddressList_Append(truth_list, cpiAddress_CreateFromInterface(i));
+ }
+
+ PARCJSONArray *json = cpiAddressList_ToJson(truth_list);
+
+ CPIAddressList *test_list = cpiAddressList_CreateFromJson(json);
+
+ assertTrue(cpiAddressList_Equals(truth_list, test_list), "Lists did not match!");
+
+ cpiAddressList_Destroy(&truth_list);
+ cpiAddressList_Destroy(&test_list);
+ parcJSONArray_Release(&json);
+}
+
+LONGBOW_TEST_CASE(Global, cpiAddressList_ToString)
+{
+ CPIAddressList *truth_list = cpiAddressList_Create();
+ int loops = 2;
+ for (int i = 0; i < loops; i++) {
+ cpiAddressList_Append(truth_list, cpiAddress_CreateFromInterface(i));
+ }
+
+ uint32_t beforeMemory = parcMemory_Outstanding();
+ char *string = cpiAddressList_ToString(truth_list);
+ assertNotNull(string, "Got null string from ToString");
+ parcMemory_Deallocate((void **) &string);
+ uint32_t afterMemory = parcMemory_Outstanding();
+
+ cpiAddressList_Destroy(&truth_list);
+
+ assertTrue(beforeMemory == afterMemory, "Memory leak from ToString by %d allocations", (int) (afterMemory - beforeMemory));
+}
+
+// ========================================
+
+LONGBOW_TEST_FIXTURE(Local)
+{
+ LONGBOW_RUN_TEST_CASE(Local, _cpiAddressList_FreeAddress);
+}
+
+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;
+}
+
+LONGBOW_TEST_CASE(Local, _cpiAddressList_FreeAddress)
+{
+ CPIAddress *address = cpiAddress_CreateFromInterface(1);
+ _cpiAddressList_FreeAddress((void **) &address);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Got memory imbalance: %u", parcMemory_Outstanding());
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_AddressList);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_CancelFlow.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_CancelFlow.c
new file mode 100644
index 00000000..8bdc4e17
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_CancelFlow.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_CancelFlow.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <inttypes.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_CancelFlow)
+{
+ // 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(cpi_CancelFlow)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_CancelFlow)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiCancelFlow_CreateRequest);
+ LONGBOW_RUN_TEST_CASE(Global, cpiCancelFlow_NameFromControlMessage);
+}
+
+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, cpiCancelFlow_CreateRequest)
+{
+ const char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"CPI_CANCEL_FLOW\":{\"FLOW_NAME\":\"ccnx:/who/doesnt/like/pie\"}}}";
+
+ CCNxName *name = ccnxName_CreateFromCString("lci:/who/doesnt/like/pie");
+ PARCJSON *cpiRequest = cpiCancelFlow_CreateRequest(name);
+ CCNxControl *controlRequest = ccnxControl_CreateCPIRequest(cpiRequest);
+
+ PARCJSON *json = ccnxControl_GetJson(controlRequest);
+
+ char buffer[1024];
+ sprintf(buffer, truth_format, cpi_GetSequenceNumber(controlRequest));
+
+ char *test_string = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(buffer, test_string) == 0, "Incorrect JSON, expected '%s' got '%s'", buffer, test_string);
+ parcMemory_Deallocate((void **) &test_string);
+
+ ccnxControl_Release(&controlRequest);
+ parcJSON_Release(&cpiRequest);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, cpiCancelFlow_NameFromControlMessage)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/who/doesnt/like/pie");
+ PARCJSON *cpiRequest = cpiCancelFlow_CreateRequest(name);
+ CCNxControl *controlRequest = ccnxControl_CreateCPIRequest(cpiRequest);
+
+ CCNxName *test_name = cpiCancelFlow_NameFromControlMessage(controlRequest);
+ assertTrue(ccnxName_Equals(test_name, name),
+ "Expected %s actual %s",
+ ccnxName_ToString(name),
+ ccnxName_ToString(test_name));
+
+ ccnxName_Release(&test_name);
+ ccnxControl_Release(&controlRequest);
+ parcJSON_Release(&cpiRequest);
+ ccnxName_Release(&name);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_CancelFlow);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Connection.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Connection.c
new file mode 100644
index 00000000..3656611f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Connection.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_Connection.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_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);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(cpi_Connection)
+{
+ 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(cpi_Connection)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_GetAddresses);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_GetIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_GetState);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnection_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, cpiConnection_Copy)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+
+ CPIConnection *copy = cpiConnection_Copy(iptun);
+
+ assertTrue(cpiConnection_GetIndex(copy) == cpiConnection_GetIndex(iptun),
+ "ifidx did not match, expected %u got %u",
+ cpiConnection_GetIndex(iptun),
+ cpiConnection_GetIndex(copy));
+
+ assertTrue(cpiConnection_GetState(copy) == cpiConnection_GetState(iptun),
+ "states did not match, expected %d got %d",
+ cpiConnection_GetState(iptun),
+ cpiConnection_GetState(copy));
+
+ assertTrue(cpiAddress_Equals(cpiConnection_GetSourceAddress(copy), cpiConnection_GetSourceAddress(iptun)),
+ "did not get same source address");
+ assertTrue(cpiAddress_Equals(cpiConnection_GetDestinationAddress(copy), cpiConnection_GetDestinationAddress(iptun)),
+ "did not get same destination address");
+
+ assertTrue(cpiConnection_GetConnectionType(copy) == cpiConnection_GetConnectionType(iptun),
+ "did not get same connection types!");
+
+ cpiConnection_Release(&copy);
+ cpiConnection_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_Create_Destroy)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_GRE);
+ cpiConnection_Release(&iptun);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Imbalance after destroying");
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_GetAddresses)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+
+ const CPIAddress *test;
+
+ test = cpiConnection_GetSourceAddress(iptun);
+ assertTrue(cpiAddress_Equals(src, test), "Address lists did not match");
+
+ test = cpiConnection_GetDestinationAddress(iptun);
+ assertTrue(cpiAddress_Equals(dst, test), "Address lists did not match");
+
+ cpiConnection_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_GetIndex)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+
+ assertTrue(cpiConnection_GetIndex(iptun) == 1, "ifidx did not match");
+
+ cpiConnection_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_GetState)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+
+ assertTrue(cpiConnection_GetState(iptun) == CPI_IFACE_UNKNOWN, "state did not match");
+
+ cpiConnection_SetState(iptun, CPI_IFACE_UP);
+ assertTrue(cpiConnection_GetState(iptun) == CPI_IFACE_UP, "state did not match");
+
+ cpiConnection_SetState(iptun, CPI_IFACE_DOWN);
+ assertTrue(cpiConnection_GetState(iptun) == CPI_IFACE_DOWN, "state did not match");
+
+ cpiConnection_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_ToJSON)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char *expected = "{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAgHBgUAAAAAAAAAAA==\"}}}";
+#elif defined(__linux__)
+ char *expected = "{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAgHBgUAAAAAAAAAAA==\"}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *iptun = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+
+ PARCJSON *test_json = cpiConnection_ToJson(iptun);
+
+ char *actual = parcJSON_ToCompactString(test_json);
+ assertTrue(strcmp(expected, actual) == 0, "Expected '%s' actual '%s'", expected, actual);
+
+ parcMemory_Deallocate((void **) &actual);
+ parcJSON_Release(&test_json);
+ cpiConnection_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnection_FromJSON)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char *input = "{\"Connection\":{\"IFIDX\":1,\"STATE\":\"UP\",\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAgHBgUAAAAAAAAAAA==\"}}}";
+#elif defined(__linux__)
+ char *input = "{\"Connection\":{\"IFIDX\":1,\"STATE\":\"UP\",\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAgHBgUAAAAAAAAAAA==\"}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIConnection *expected = cpiConnection_Create(1, src, dst, cpiConnection_TCP);
+ cpiConnection_SetState(expected, CPI_IFACE_UP);
+
+ PARCJSON *json = parcJSON_ParseString(input);
+
+ CPIConnection *actual = cpiConnection_CreateFromJson(json);
+ assertTrue(cpiConnection_Equals(expected, actual), "Connection interfaces do not match");
+
+ parcJSON_Release(&json);
+ cpiConnection_Release(&expected);
+ cpiConnection_Release(&actual);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_Connection);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionEthernet.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionEthernet.c
new file mode 100644
index 00000000..4646eccf
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionEthernet.c
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_ConnectionEthernet.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+typedef struct test_data {
+ CPIConnectionEthernet *etherConn;
+
+ // the truth values of the connection
+ uint8_t macArray[6];
+ CPIAddress *macAddress;
+ uint16_t ethertype;
+ char ifname[16];
+ char symbolic[16];
+} TestData;
+
+static CPIConnectionEthernet *
+conjureObject(uint8_t mac[6], uint16_t ethertype, const char *ifname, const char *symbolic)
+{
+ CPIAddress *macAddress = cpiAddress_CreateFromLink(mac, 6);
+ CPIConnectionEthernet *etherConn = cpiConnectionEthernet_Create(ifname, macAddress, ethertype, symbolic);
+ cpiAddress_Destroy(&macAddress);
+ return etherConn;
+}
+
+static TestData *
+_commonSetup(void)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+ assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData));
+
+ uint8_t mac[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 };
+ memcpy(data->macArray, mac, 6);
+ data->macAddress = cpiAddress_CreateFromLink(data->macArray, 6);
+ data->ethertype = 0x0801;
+ sprintf(data->ifname, "em1");
+ sprintf(data->symbolic, "conn0");
+
+ data->etherConn = cpiConnectionEthernet_Create(data->ifname, data->macAddress, data->ethertype, data->symbolic);
+ return data;
+}
+
+static void
+_commonTeardown(TestData *data)
+{
+ cpiConnectionEthernet_Release(&data->etherConn);
+ cpiAddress_Destroy(&data->macAddress);
+ parcMemory_Deallocate((void **) &data);
+}
+
+LONGBOW_TEST_RUNNER(cpi_ConnectionEthernet)
+{
+ // 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(cpi_ConnectionEthernet)
+{
+ 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(cpi_ConnectionEthernet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_Create);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_CreateAddMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_CreateRemoveMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_FromControl);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_GetPeerLinkAddress);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_GetEthertype);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_GetEthertype);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_GetInterfaceName);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_IsAddMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_IsRemoveMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionEthernet_Equals);
+}
+
+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, cpiConnectionEthernet_Create)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ size_t beforeBalance = parcMemory_Outstanding();
+ CPIConnectionEthernet *etherConn = cpiConnectionEthernet_Create(data->ifname, data->macAddress, data->ethertype, data->symbolic);
+ cpiConnectionEthernet_Release(&etherConn);
+ size_t afterBalance = parcMemory_Outstanding();
+
+ assertTrue(afterBalance == beforeBalance,
+ "Memory imbalance on create/destroy, before %zu afer %zu",
+ beforeBalance, afterBalance);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_Equals)
+{
+ uint8_t mac_a[6] = { 1, 2, 3, 4, 5, 6 };
+ uint8_t mac_b[6] = { 9, 8, 7, 6, 5, 4 };
+
+ CPIConnectionEthernet *x = conjureObject(mac_a, 0x0123, "happy", "puppy");
+ CPIConnectionEthernet *y = conjureObject(mac_a, 0x0123, "happy", "puppy");
+ CPIConnectionEthernet *z = conjureObject(mac_a, 0x0123, "happy", "puppy");
+
+ CPIConnectionEthernet *u = conjureObject(mac_b, 0x0123, "happy", "puppy");
+ CPIConnectionEthernet *v = conjureObject(mac_a, 0x7777, "happy", "puppy");
+ CPIConnectionEthernet *w = conjureObject(mac_a, 0x0123, "sad", "kitten");
+
+ assertEqualsContract(cpiConnectionEthernet_Equals, x, y, z, u, v, w);
+
+ cpiConnectionEthernet_Release(&x);
+ cpiConnectionEthernet_Release(&y);
+ cpiConnectionEthernet_Release(&z);
+ cpiConnectionEthernet_Release(&u);
+ cpiConnectionEthernet_Release(&v);
+ cpiConnectionEthernet_Release(&w);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_CreateAddMessage)
+{
+ const char *truthStringFormat = "{ \"CPI_REQUEST\" : { \"SEQUENCE\" : %d, \"%s\" : { \"IFNAME\" : \"em1\", \"SYMBOLIC\" : \"conn0\", \"PEER_ADDR\" : { \"ADDRESSTYPE\" : \"LINK\", \"DATA\" : \"AQIDBAUG\" }, \"ETHERTYPE\" : 2049 } } }";
+
+ char buffer[1024];
+
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ CCNxControl *control = cpiConnectionEthernet_CreateAddMessage(data->etherConn);
+
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *testJson = ccnxControl_GetJson(control);
+ assertNotNull(testJson, "Got null json from control message");
+ uint64_t seqnum = controlPlaneInterface_GetSequenceNumber(testJson);
+ sprintf(buffer, truthStringFormat, (int) seqnum, KEY_ADDETHER);
+
+ PARCJSON *truthJson = parcJSON_ParseString(buffer);
+ assertTrue(parcJSON_Equals(truthJson, testJson), "JSON not correct in Add Connection Ethernet")
+ {
+ char *a = parcJSON_ToString(testJson);
+ printf("Got: \n%s\n", a);
+ parcMemory_Deallocate((void **) &a);
+
+ printf("Expected\n%s\n", buffer);
+ }
+
+ parcJSON_Release(&truthJson);
+ }
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_CreateRemoveMessage)
+{
+ const char *truthStringFormat = "{ \"CPI_REQUEST\" : { \"SEQUENCE\" : %d, \"%s\" : { \"IFNAME\" : \"em1\", \"SYMBOLIC\" : \"conn0\", \"PEER_ADDR\" : { \"ADDRESSTYPE\" : \"LINK\", \"DATA\" : \"AQIDBAUG\" }, \"ETHERTYPE\" : 2049 } } }";
+
+ char buffer[1024];
+
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ CCNxControl *control = cpiConnectionEthernet_CreateRemoveMessage(data->etherConn);
+
+ if (ccnxControl_IsCPI(control)) {
+ PARCJSON *testJson = ccnxControl_GetJson(control);
+ assertNotNull(testJson, "Got null json from control message");
+ uint64_t seqnum = controlPlaneInterface_GetSequenceNumber(testJson);
+ sprintf(buffer, truthStringFormat, (int) seqnum, KEY_REMOVEETHER);
+
+ PARCJSON *truthJson = parcJSON_ParseString(buffer);
+ assertTrue(parcJSON_Equals(truthJson, testJson), "JSON not correct in Remove Connection Ethernet")
+ {
+ char *a = parcJSON_ToString(testJson);
+ printf("Got: \n%s\n", a);
+ parcMemory_Deallocate((void **) &a);
+
+ printf("Expected\n%s\n", buffer);
+ }
+
+ parcJSON_Release(&truthJson);
+ }
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_FromControl)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxControl *addMessage = cpiConnectionEthernet_CreateAddMessage(data->etherConn);
+
+ CPIConnectionEthernet *test = cpiConnectionEthernet_FromControl(addMessage);
+
+ assertNotNull(test, "Got null object parsing json: %s\n", parcJSON_ToString(ccnxControl_GetJson(addMessage)));
+
+ assertTrue(cpiConnectionEthernet_Equals(test, data->etherConn), "Object from control did not equal true value");
+
+ cpiConnectionEthernet_Release(&test);
+ ccnxControl_Release(&addMessage);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_GetPeerLinkAddress)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CPIAddress *test = cpiConnectionEthernet_GetPeerLinkAddress(data->etherConn);
+ assertTrue(cpiAddress_Equals(test, data->macAddress), "Wrong mac address");
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_GetEthertype)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ uint16_t test = cpiConnectionEthernet_GetEthertype(data->etherConn);
+ assertTrue(test == data->ethertype, "Wrong ethertype, got %04X expected %04X", test, data->ethertype);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_GetInterfaceName)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ const char *test = cpiConnectionEthernet_GetInterfaceName(data->etherConn);
+ assertTrue(strcmp(test, data->ifname) == 0, "Wrong interface name, got '%s' expected '%s'", test, data->ifname);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_IsAddMessage)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ CCNxControl *control = cpiConnectionEthernet_CreateAddMessage(data->etherConn);
+ bool isAdd = cpiConnectionEthernet_IsAddMessage(control);
+ ccnxControl_Release(&control);
+
+ assertTrue(isAdd, "Add Connection Ethernet message did not report as such a message.");
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionEthernet_IsRemoveMessage)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ CCNxControl *control = cpiConnectionEthernet_CreateRemoveMessage(data->etherConn);
+ bool isRemove = cpiConnectionEthernet_IsRemoveMessage(control);
+ ccnxControl_Release(&control);
+
+ assertTrue(isRemove, "Remove Connection Ethernet message did not report as such a message.");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_ConnectionEthernet);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionList.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionList.c
new file mode 100644
index 00000000..c624866a
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ConnectionList.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.
+ */
+
+/*
+ *
+ */
+#include "../cpi_ConnectionList.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_ConnectionList)
+{
+ // 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(cpi_ConnectionList)
+{
+ 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(cpi_ConnectionList)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionList_Append);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionList_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionList_FromJson);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionList_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, cpiConnectionList_ToJson);
+}
+
+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 CPIConnection *
+createConnectionObject(unsigned ifidx, int s_addr, uint16_t s_port, int d_addr, uint16_t d_port)
+{
+ return cpiConnection_Create(ifidx,
+ cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_family = PF_INET, .sin_addr.s_addr = s_addr, .sin_port = s_port }),
+ cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_family = PF_INET, .sin_addr.s_addr = d_addr, .sin_port = d_port }),
+ cpiConnection_TCP);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionList_Append)
+{
+ CPIConnectionList *list = cpiConnectionList_Create();
+ cpiConnectionList_Append(list, createConnectionObject(1, 2, 3, 4, 5));
+
+ assertTrue(parcArrayList_Size(list->listOfConnections) == 1, "got wrong size, expected %u got %zu", 1, parcArrayList_Size(list->listOfConnections));
+
+ cpiConnectionList_Destroy(&list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionList_Create_Destroy)
+{
+ CPIConnectionList *list = cpiConnectionList_Create();
+ cpiConnectionList_Destroy(&list);
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after create/destroy");
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionList_FromJson)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_string[] = "{\"ConnectionList\":[{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIDAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#elif defined(__linux__)
+ char truth_string[] = "{\"ConnectionList\":[{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgADAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CPIConnectionList *truth_list = cpiConnectionList_Create();
+ cpiConnectionList_Append(truth_list, createConnectionObject(1, 2, 3, 4, 5));
+
+ PARCJSON *truth_json = parcJSON_ParseString(truth_string);
+ CPIConnectionList *test_list = cpiConnectionList_FromJson(truth_json);
+
+ assertTrue(cpiConnectionList_Equals(truth_list, test_list), "Lists do not match");
+
+ cpiConnectionList_Destroy(&test_list);
+ parcJSON_Release(&truth_json);
+ cpiConnectionList_Destroy(&truth_list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionList_Equals)
+{
+ CPIConnectionList *list_a = cpiConnectionList_Create();
+ cpiConnectionList_Append(list_a, createConnectionObject(1, 2, 3, 4, 5));
+
+ CPIConnectionList *list_b = cpiConnectionList_Create();
+ cpiConnectionList_Append(list_b, createConnectionObject(1, 2, 3, 4, 5));
+
+ CPIConnectionList *list_c = cpiConnectionList_Create();
+ cpiConnectionList_Append(list_c, createConnectionObject(1, 2, 3, 4, 5));
+
+ CPIConnectionList *unequal = cpiConnectionList_Create();
+ cpiConnectionList_Append(unequal, createConnectionObject(99, 2, 3, 4, 5));
+ cpiConnectionList_Append(unequal, createConnectionObject(1, 99, 3, 4, 5));
+ cpiConnectionList_Append(unequal, createConnectionObject(1, 2, 99, 4, 5));
+ cpiConnectionList_Append(unequal, createConnectionObject(1, 2, 3, 99, 5));
+ cpiConnectionList_Append(unequal, createConnectionObject(1, 2, 3, 4, 99));
+
+ assertEqualsContract(cpiConnectionList_Equals, list_a, list_b, list_c, unequal);
+
+ cpiConnectionList_Destroy(&unequal);
+ cpiConnectionList_Destroy(&list_a);
+ cpiConnectionList_Destroy(&list_b);
+ cpiConnectionList_Destroy(&list_c);
+}
+
+LONGBOW_TEST_CASE(Global, cpiConnectionList_ToJson)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_string[] = "{\"ConnectionList\":[{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIDAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#elif defined(__linux__)
+ char truth_string[] = "{\"ConnectionList\":[{\"Connection\":{\"IFIDX\":1,\"CONNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgADAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+
+ CPIConnectionList *list = cpiConnectionList_Create();
+ cpiConnectionList_Append(list, createConnectionObject(1, 2, 3, 4, 5));
+
+ PARCJSON *json = cpiConnectionList_ToJson(list);
+ char *test = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(truth_string, test) == 0, "Got wrong JSON.\nexpected: %s\ngot %s\n", truth_string, test);
+ parcMemory_Deallocate((void **) &test);
+
+ parcJSON_Release(&json);
+ cpiConnectionList_Destroy(&list);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_ConnectionList);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlFacade.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlFacade.c
new file mode 100644
index 00000000..17e04299
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlFacade.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_ControlFacade.c"
+
+#include <parc/algol/parc_SafeMemory.h>
+
+#include <ccnx/transport/common/transport_MetaMessage.h>
+
+#include <LongBow/unit-test.h>
+
+typedef struct test_data {
+ char *jsonstring;
+ PARCJSON *json;
+} TestData;
+
+static TestData *
+_commonSetup(void)
+{
+ TestData *data = parcMemory_AllocateAndClear(sizeof(TestData));
+ assertNotNull(data, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(TestData));
+ data->jsonstring = parcMemory_StringDuplicate("{ \"EMPTY\": \"NESS\" }", 100);
+ data->json = parcJSON_ParseString(data->jsonstring);
+ assertNotNull(data->json, "got null JSON from string: %s", data->jsonstring);
+
+ return data;
+}
+
+static void
+_commonTeardown(TestData *data)
+{
+ parcJSON_Release(&data->json);
+ parcMemory_Deallocate((void **) &(data->jsonstring));
+ parcMemory_Deallocate((void **) &data);
+}
+
+LONGBOW_TEST_RUNNER(ccnx_ControlFacade)
+{
+ // 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(ccnx_ControlFacade)
+{
+ 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_ControlFacade)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_CreateControlMessage_Notification);
+
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_AssertValid);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_CreateCPI);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_CreateNotification);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_GetJson);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_IsCPI);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_IsNotification);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_Display);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControlFacade_ToString);
+}
+
+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, ccnxControlFacade_CreateControlMessage_Notification)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxMetaMessage *control = ccnxControlFacade_CreateNotification(data->json);
+
+ CCNxControl *cpiControl = ccnxMetaMessage_GetControl(control);
+
+ assertNotNull(cpiControl, "Got null control message");
+
+ ccnxMetaMessage_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_AssertValid)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateCPI(data->json);
+ ccnxControlFacade_AssertValid(control);
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_CreateCPI)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateCPI(data->json);
+ ccnxControlFacade_AssertValid(control);
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_CreateNotification)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateNotification(data->json);
+ ccnxControlFacade_AssertValid(control);
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_GetJson)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateNotification(data->json);
+
+ PARCJSON *json = ccnxControlFacade_GetJson(control);
+
+ char *test = parcJSON_ToCompactString(json);
+ char *truth = parcJSON_ToCompactString(data->json);
+
+ assertTrue(strcmp(test, truth) == 0, "Wrong JSON\ngot %s\nexpected %s\n", test, truth);
+
+ parcMemory_Deallocate((void **) &test);
+ parcMemory_Deallocate((void **) &truth);
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_IsCPI)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateCPI(data->json);
+ assertTrue(ccnxControlFacade_IsCPI(control), "Notification says its not a notification");
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_IsNotification)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateNotification(data->json);
+ assertTrue(ccnxControlFacade_IsNotification(control), "Notification says its not a notification");
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_Display)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateNotification(data->json);
+ ccnxControlFacade_Display(control, 1);
+ ccnxTlvDictionary_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControlFacade_ToString)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+ CCNxTlvDictionary *control = ccnxControlFacade_CreateNotification(data->json);
+ char *desc = ccnxControlFacade_ToString(control);
+
+ assertNotNull(desc, "Expected a string");
+ printf("%s\n", desc);
+
+ parcMemory_Deallocate((void **) &desc);
+ ccnxTlvDictionary_Release(&control);
+}
+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_ControlFacade);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlMessage.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlMessage.c
new file mode 100644
index 00000000..82b687b2
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ControlMessage.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_ControlMessage.c"
+
+#include <stdio.h>
+
+#include <arpa/inet.h>
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <ccnx/api/control/cpi_InterfaceIPTunnel.h>
+
+LONGBOW_TEST_RUNNER(cpi_ControlMessage)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(cpi_ControlMessage)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_ControlMessage)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_AcquireRelease);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateAddRouteRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateAddRouteToSelfRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateCPIRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateCancelFlowRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateConnectionListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateIPTunnelRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateInterfaceListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreatePauseInputRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateRemoveRouteRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateRemoveRouteToSelfRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_CreateRouteListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_Display);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_GetAckOriginalSequenceNumber);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_GetJson);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_GetNotifyStatus);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_IsACK);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_IsCPI);
+ LONGBOW_RUN_TEST_CASE(Global, ccnxControl_IsNotification);
+}
+
+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;
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_AcquireRelease)
+{
+ CCNxControl *control = ccnxControl_CreateInterfaceListRequest();
+ CCNxControl *reference = ccnxControl_Acquire(control);
+
+ ccnxControl_Release(&control);
+
+ assertNull(control, "Expected control to be null");
+ assertNotNull(reference, "Expected acquired reference to be non null");
+ ccnxControl_Release(&reference);
+ assertNull(reference, "Expected reference to be null");
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateAddRouteRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ CCNxControl *control = ccnxControl_CreateAddRouteRequest(route);
+
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_REGISTER_PREFIX,
+ "Expected operation %d got %d", CPI_REGISTER_PREFIX, cpi_getCPIOperation2(json));
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateAddRouteToSelfRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ CCNxControl *control = ccnxControl_CreateAddRouteToSelfRequest(name);
+
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_REGISTER_PREFIX,
+ "Expected operation %d got %d", CPI_REGISTER_PREFIX, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+}
+
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateCancelFlowRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ CCNxControl *control = ccnxControl_CreateCancelFlowRequest(name);
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_CANCEL_FLOW,
+ "Expected operation %d got %d", CPI_CANCEL_FLOW, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreatePauseInputRequest)
+{
+ CCNxControl *control = ccnxControl_CreatePauseInputRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_PAUSE,
+ "Expected operation %d got %d", CPI_PAUSE, cpi_getCPIOperation2(json));
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateConnectionListRequest)
+{
+ CCNxControl *control = ccnxControl_CreateConnectionListRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_CONNECTION_LIST,
+ "Expected operation %d got %d", CPI_CONNECTION_LIST, cpi_getCPIOperation2(json));
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateIPTunnelRequest)
+{
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(9999);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ CPIInterfaceIPTunnel *tunnel = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP, "tun0");
+ CCNxControl *control = ccnxControl_CreateIPTunnelRequest(tunnel);
+
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_CREATE_TUNNEL,
+ "Expected operation %d got %d", CPI_CREATE_TUNNEL, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+ cpiInterfaceIPTunnel_Release(&tunnel);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateInterfaceListRequest)
+{
+ CCNxControl *control = ccnxControl_CreateInterfaceListRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_INTERFACE_LIST,
+ "Expected operation %d got %d", CPI_INTERFACE_LIST, cpi_getCPIOperation2(json));
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateRemoveRouteRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ CPIRouteEntry *route = cpiRouteEntry_CreateRouteToSelf(name);
+ CCNxControl *control = ccnxControl_CreateRemoveRouteRequest(route);
+
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_UNREGISTER_PREFIX,
+ "Expected operation %d got %d", CPI_UNREGISTER_PREFIX, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateRemoveRouteToSelfRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ CCNxControl *control = ccnxControl_CreateRemoveRouteToSelfRequest(name);
+
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_UNREGISTER_PREFIX,
+ "Expected operation %d got %d", CPI_UNREGISTER_PREFIX, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateRouteListRequest)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertTrue(cpi_getCPIOperation2(json) == CPI_PREFIX_REGISTRATION_LIST,
+ "Expected operation %d got %d", CPI_PREFIX_REGISTRATION_LIST, cpi_getCPIOperation2(json));
+
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_CreateCPIRequest)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+ PARCJSON *cpiRequest = cpiCancelFlow_CreateRequest(name);
+
+ CCNxControl *control = ccnxControl_CreateCPIRequest(cpiRequest);
+
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ parcJSON_Release(&cpiRequest);
+ ccnxControl_Release(&control);
+ ccnxName_Release(&name);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_Display)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ ccnxControl_Display(control, 4);
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_GetAckOriginalSequenceNumber)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ PARCJSON *jsonAck = cpiAcks_CreateAck(json);
+
+ CCNxControl *response = ccnxControl_CreateCPIRequest(jsonAck);
+
+ // Calling GetAckOriginalSequenceNumber() to make sure the path works. We don't care
+ // about the value.
+ /*uint64_t originalSequenceNumber = */ ccnxControl_GetAckOriginalSequenceNumber(response);
+
+ ccnxControl_Release(&control);
+ ccnxControl_Release(&response);
+ parcJSON_Release(&jsonAck);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_GetJson)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ PARCJSON *json = ccnxControl_GetJson(control);
+ assertNotNull(json, "Expected some JSON");
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_IsACK)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ assertNotNull(control, "Expected control message to be non null");
+ assertTrue(ccnxControl_IsCPI(control), "Expected control to be a CPI control message");
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ PARCJSON *jsonAck = cpiAcks_CreateAck(json);
+
+ CCNxControl *response = ccnxControl_CreateCPIRequest(jsonAck);
+
+ assertTrue(ccnxControl_IsACK(response), "Expected the message to be an Ack");
+
+ ccnxControl_Release(&control);
+ ccnxControl_Release(&response);
+ parcJSON_Release(&jsonAck);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_IsCPI)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+ assertTrue(ccnxControl_IsCPI(control), "Expected a CPI Message");
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_IsNotification)
+{
+ PARCJSON *json = parcJSON_Create();
+ CCNxMetaMessage *notification = ccnxControlFacade_CreateNotification(json);
+ CCNxControl *control = ccnxMetaMessage_GetControl(notification);
+
+ assertTrue(ccnxControl_IsNotification(control), "Expected a notification");
+ assertFalse(ccnxControl_IsCPI(control), "Did not expect a CPI command");
+
+ parcJSON_Release(&json);
+ ccnxTlvDictionary_Release(&notification);
+}
+
+LONGBOW_TEST_CASE(Global, ccnxControl_GetNotifyStatus)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/boose/roo/pie");
+
+ NotifyStatus *expected = notifyStatus_Create(1, notifyStatusCode_CONNECTION_OPEN, name, "There's a spider behind you.");
+
+ PARCJSON *json = notifyStatus_ToJSON(expected);
+ CCNxMetaMessage *notification = ccnxControlFacade_CreateNotification(json);
+ CCNxControl *control = ccnxMetaMessage_GetControl(notification);
+
+ assertTrue(ccnxControl_IsNotification(control), "Expected a notification");
+ NotifyStatus *status = ccnxControl_GetNotifyStatus(control);
+
+ assertTrue(ccnxName_Equals(notifyStatus_GetName(expected), notifyStatus_GetName(status)), "Expected equal names");
+ assertTrue(notifyStatus_GetStatusCode(expected) == notifyStatus_GetStatusCode(status), "Expected equal status codes");
+ assertTrue(strcmp(notifyStatus_GetMessage(expected), notifyStatus_GetMessage(status)) == 0, "Expected equal messages");
+
+ parcJSON_Release(&json);
+ ccnxTlvDictionary_Release(&notification);
+ ccnxName_Release(&name);
+ notifyStatus_Release(&status);
+ notifyStatus_Release(&expected);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_ControlMessage);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Forwarding.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Forwarding.c
new file mode 100644
index 00000000..ecaa569f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Forwarding.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_Forwarding.c"
+
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+#include <inttypes.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_Forwarding)
+{
+ // 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(cpi_Forwarding)
+{
+ 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(cpi_Forwarding)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_AddRoute_1);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_AddRoute_2);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_AddRoute_3);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_AddRouteJsonTag);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_AddRouteToSelf);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_RemoveRoute);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_RemoveRouteJsonTag);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_RemoveRouteToSelf);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_RouteFromControlMessage);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_CreateRouteListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, cpiForwarding_RouteListFromControlMessage);
+}
+
+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;
+}
+
+/**
+ * Add route with all options
+ */
+LONGBOW_TEST_CASE(Global, cpiForwarding_AddRoute_1)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200,\"LIFETIME\":[3600,0]}}}";
+#elif defined(__linux__)
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200,\"LIFETIME\":[3600,0]}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ CCNxControl *control = ccnxControl_CreateAddRouteRequest(route);
+
+ // get its sequence number
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Expected '%s', actual '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ cpiRouteEntry_Destroy(&route);
+ cpiAddress_Destroy(&nexthop);
+}
+
+/**
+ * Add route without lifeitme
+ */
+LONGBOW_TEST_CASE(Global, cpiForwarding_AddRoute_2)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}";
+#elif defined(__linux__)
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ CCNxControl *control = ccnxControl_CreateAddRouteRequest(route);
+
+ // get its sequence number
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Expected '%s', actual '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ cpiRouteEntry_Destroy(&route);
+ cpiAddress_Destroy(&nexthop);
+}
+
+/**
+ * Add route without lifeitme or nexthop
+ */
+LONGBOW_TEST_CASE(Global, cpiForwarding_AddRoute_3)
+{
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}";
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ CCNxControl *control = ccnxControl_CreateAddRouteRequest(route);
+
+ // get its sequence number
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Control message json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ cpiRouteEntry_Destroy(&route);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_AddRouteJsonTag)
+{
+ const char *tag = cpiForwarding_AddRouteJsonTag();
+ assertTrue(strcmp(tag, cpiRegister) == 0, "cpiForwarding_AddRouteJsonTag not using defined value %s", cpiRegister);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_AddRouteToSelf)
+{
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"REGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":2147483647,\"FLAGS\":0,\"PROTOCOL\":\"LOCAL\",\"ROUTETYPE\":\"LONGEST\",\"COST\":0}}}";
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ CCNxControl *control = ccnxControl_CreateAddRouteToSelfRequest(prefix);
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Control message json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&prefix);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_RemoveRoute)
+{
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"UNREGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}}}";
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ unsigned cost = 200;
+
+ CPIRouteEntry *route =
+ cpiRouteEntry_Create(prefix, ifidx, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+
+ CCNxControl *control = ccnxControl_CreateRemoveRouteRequest(route);
+
+ // get its sequence number
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Control message json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_RemoveRouteJsonTag)
+{
+ const char *tag = cpiForwarding_RemoveRouteJsonTag();
+ assertTrue(strcmp(tag, cpiUnregister) == 0, "cpiForwarding_AddRouteJsonTag not using defined value %s", cpiUnregister);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_RemoveRouteToSelf)
+{
+ char truth_format[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"UNREGISTER\":{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":2147483647,\"FLAGS\":0,\"PROTOCOL\":\"LOCAL\",\"ROUTETYPE\":\"LONGEST\",\"COST\":0}}}";
+ char truth[1024];
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ CCNxControl *control = ccnxControl_CreateRemoveRouteToSelfRequest(prefix);
+
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+ sprintf(truth, truth_format, seqnum);
+
+ PARCJSON *test_json = ccnxControl_GetJson(control);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Control message json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ ccnxControl_Release(&control);
+ ccnxName_Release(&prefix);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_RouteFromControlMessage)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ CCNxControl *control = ccnxControl_CreateAddRouteRequest(route);
+
+ CPIRouteEntry *test_route = cpiForwarding_RouteFromControlMessage(control);
+
+ assertTrue(cpiRouteEntry_Equals(route, test_route),
+ "messages not equa: expected %s got %s",
+ parcJSON_ToCompactString(cpiRouteEntry_ToJson(route)),
+ parcJSON_ToCompactString(cpiRouteEntry_ToJson(test_route)));
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+ cpiRouteEntry_Destroy(&test_route);
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_CreateRouteListRequest)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+
+ assertTrue(ccnxControl_IsCPI(control), "Control message not a CPI message");
+ assertTrue(cpi_GetMessageOperation(control) == CPI_PREFIX_REGISTRATION_LIST, "Message not a prefix regisration list");
+
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiForwarding_RouteListFromControlMessage)
+{
+ CCNxControl *control = ccnxControl_CreateRouteListRequest();
+
+ CPIRouteEntryList *routeList = cpiRouteEntryList_Create();
+ PARCJSON *json = cpiRouteEntryList_ToJson(routeList);
+ CCNxControl *response = cpi_CreateResponse(control, json);
+ parcJSON_Release(&json);
+ CPIRouteEntryList *test = cpiForwarding_RouteListFromControlMessage(response);
+ assertTrue(cpiRouteEntryList_Equals(routeList, test), "Route lists not equal");
+
+ ccnxControl_Release(&control);
+ ccnxControl_Release(&response);
+ cpiRouteEntryList_Destroy(&routeList);
+ cpiRouteEntryList_Destroy(&test);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_Forwarding);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Interface.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Interface.c
new file mode 100644
index 00000000..b1c7b553
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Interface.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_Interface.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_Interface)
+{
+ // 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(cpi_Interface)
+{
+ 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(cpi_Interface)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ==================================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_AddAddress);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_GetAddresses);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_GetMtu);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_GetInterfaceIndex);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_NameEquals_IsEqual);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_NameEquals_IsNotEqual);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_ToJson);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_FromJson);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_IsEqual);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_BothNull);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_OneNull);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalName);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalLoopback);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalMulticast);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalMTU);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_Equals_UnequalAddresses);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterface_ToString);
+}
+
+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, cpiInterface_Create_Destroy)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_Destroy(&iface);
+
+ assertTrue(parcSafeMemory_ReportAllocation(STDOUT_FILENO) == 0, "Memory imbalance on create/destroy");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_AddAddress)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ assertTrue(cpiAddressList_Length(iface->addressList) == 1,
+ "Incorrect address list length, expected %u got %zu",
+ 1,
+ cpiAddressList_Length(iface->addressList));
+
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(2));
+ assertTrue(cpiAddressList_Length(iface->addressList) == 2,
+ "Incorrect address list length, expected %u got %zu",
+ 2,
+ cpiAddressList_Length(iface->addressList));
+
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_GetAddresses)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(2));
+
+ const CPIAddressList *list = cpiInterface_GetAddresses(iface);
+ assertTrue(cpiAddressList_Length(list) == 2, "Incorrect list size, expected %u got %zu", 2, cpiAddressList_Length(list));
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_GetMtu)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+
+ unsigned test = cpiInterface_GetMTU(iface);
+ assertTrue(test == 1500, "Wrong MTU expected 1500 got %u", test);
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_GetInterfaceIndex)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(2));
+
+ unsigned testvalue = cpiInterface_GetInterfaceIndex(iface);
+
+ assertTrue(testvalue == 1, "Incorrect interfaceIndex, expected %u got %u", 1, testvalue);
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_NameEquals_IsEqual)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ assertTrue(cpiInterface_NameEquals(iface, "eth0"), "name did not compare as equal");
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_NameEquals_IsNotEqual)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ assertFalse(cpiInterface_NameEquals(iface, "eth2"), "Unequal names compare as equal");
+ cpiInterface_Destroy(&iface);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiInterface_ToJson)
+{
+ char truth[] = "{\"Interface\":{\"Name\":\"eth0\",\"Index\":1,\"Loopback\":\"true\",\"Multicast\":\"false\",\"MTU\":1500,\"Addrs\":[{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAQ==\"},{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAAAg==\"}]}}";
+
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(2));
+
+ PARCJSON *json = cpiInterface_ToJson(iface);
+
+ char *str = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(str, truth) == 0, "JSON mismatch, expected '%s' got '%s'", truth, str);
+ parcMemory_Deallocate((void **) &str);
+ parcJSON_Release(&json);
+ cpiInterface_Destroy(&iface);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_FromJson)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(2));
+
+ PARCJSON *json = cpiInterface_ToJson(iface);
+
+ CPIInterface *test_iface = cpiInterface_FromJson(json);
+
+ assertTrue(cpiInterface_Equals(iface, test_iface), "Interface from json not equal to truth");
+
+ cpiInterface_Destroy(&test_iface);
+ cpiInterface_Destroy(&iface);
+ parcJSON_Release(&json);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_IsEqual)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertTrue(cpiInterface_Equals(iface_a, iface_b), "Two equal interfaces did not compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_BothNull)
+{
+ assertTrue(cpiInterface_Equals(NULL, NULL), "Two NULL interfaces did not compare equal");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_OneNull)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, NULL), "One null one non-null interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalName)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth1", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalIndex)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 2, true, false, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalLoopback)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 1, false, false, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalMulticast)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 1, true, true, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalMTU)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 1, true, false, 9000);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_Equals_UnequalAddresses)
+{
+ CPIInterface *iface_a = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(1));
+ cpiInterface_AddAddress(iface_a, cpiAddress_CreateFromInterface(2));
+
+ CPIInterface *iface_b = cpiInterface_Create("eth0", 1, true, false, 1500);
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(3));
+ cpiInterface_AddAddress(iface_b, cpiAddress_CreateFromInterface(2));
+
+ assertFalse(cpiInterface_Equals(iface_a, iface_b), "Two unequal interfaces compare equal");
+
+ cpiInterface_Destroy(&iface_b);
+ cpiInterface_Destroy(&iface_a);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterface_ToString)
+{
+ CPIInterface *iface = cpiInterface_Create("eth0", 1, false, true, 1500);
+ cpiInterface_AddAddress(iface, cpiAddress_CreateFromInterface(1));
+
+ uint32_t beforeBalance = parcMemory_Outstanding();
+ char *string = cpiInterface_ToString(iface);
+ parcMemory_Deallocate((void **) &string);
+ uint32_t afterBalance = parcMemory_Outstanding();
+ cpiInterface_Destroy(&iface);
+
+ assertTrue(beforeBalance == afterBalance, "Memory leak: off by %d allocations", (int) (afterBalance - beforeBalance));
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_Interface);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceEthernet.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceEthernet.c
new file mode 100644
index 00000000..e96fc76f
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceEthernet.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 "../cpi_InterfaceEthernet.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceEthernet)
+{
+ // 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(cpi_InterfaceEthernet)
+{
+ 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(cpi_InterfaceEthernet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_GetAddresses);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_GetIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_GetState);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceEthernet_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, cpiInterfaceEthernet_Copy)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, list);
+
+ CPIInterfaceEthernet *copy = cpiInterfaceEthernet_Copy(ethernet);
+
+ assertTrue(cpiInterfaceEthernet_GetIndex(copy) == cpiInterfaceEthernet_GetIndex(ethernet),
+ "ifidx did not match, expected %u got %u",
+ cpiInterfaceEthernet_GetIndex(ethernet),
+ cpiInterfaceEthernet_GetIndex(copy));
+
+ assertTrue(cpiInterfaceEthernet_GetState(copy) == cpiInterfaceEthernet_GetState(ethernet),
+ "states did not match, expected %d got %d",
+ cpiInterfaceEthernet_GetState(ethernet),
+ cpiInterfaceEthernet_GetState(copy));
+
+ assertTrue(cpiAddressList_Equals(cpiInterfaceEthernet_GetAddresses(copy), cpiInterfaceEthernet_GetAddresses(ethernet)), "did not get same addresses");
+
+ cpiInterfaceEthernet_Destroy(&copy);
+ cpiInterfaceEthernet_Destroy(&ethernet);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_Create_Destroy)
+{
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, cpiAddressList_Create());
+ cpiInterfaceEthernet_Destroy(&ethernet);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Imbalance after destroying");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_GetAddresses)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, list);
+
+ const CPIAddressList *test = cpiInterfaceEthernet_GetAddresses(ethernet);
+ assertTrue(cpiAddressList_Equals(list, test), "Address lists did not match");
+
+ cpiInterfaceEthernet_Destroy(&ethernet);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_GetIndex)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, list);
+
+ assertTrue(cpiInterfaceEthernet_GetIndex(ethernet) == 1, "ifidx did not match");
+
+ cpiInterfaceEthernet_Destroy(&ethernet);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_GetState)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, list);
+
+ assertTrue(cpiInterfaceEthernet_GetState(ethernet) == CPI_IFACE_UNKNOWN, "state did not match");
+
+ cpiInterfaceEthernet_SetState(ethernet, CPI_IFACE_UP);
+ assertTrue(cpiInterfaceEthernet_GetState(ethernet) == CPI_IFACE_UP, "state did not match");
+
+ cpiInterfaceEthernet_SetState(ethernet, CPI_IFACE_DOWN);
+ assertTrue(cpiInterfaceEthernet_GetState(ethernet) == CPI_IFACE_DOWN, "state did not match");
+
+ cpiInterfaceEthernet_Destroy(&ethernet);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_ToJSON)
+{
+ char truth_json_str[] = "{\"ETHERNET\":{\"IFIDX\":1,\"ADDRS\":[{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAABQ==\"},{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAADw==\"}]}}";
+
+ CPIAddress *addr5 = cpiAddress_CreateFromInterface(5);
+ CPIAddress *addr15 = cpiAddress_CreateFromInterface(15);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Append(cpiAddressList_Create(), addr5), addr15);
+
+ CPIInterfaceEthernet *ethernet = cpiInterfaceEthernet_Create(1, list);
+
+ PARCJSON *test_json = cpiInterfaceEthernet_ToJson(ethernet);
+ char *test_json_str = parcJSON_ToCompactString(test_json);
+ assertTrue(strcmp(truth_json_str, test_json_str) == 0, "JSON strings do not match");
+
+ parcMemory_Deallocate((void **) &test_json_str);
+ parcJSON_Release(&test_json);
+ cpiInterfaceEthernet_Destroy(&ethernet);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceEthernet_FromJSON)
+{
+ char truth_json_str[] = "{\"ETHERNET\":{\"IFIDX\":1,\"STATE\":\"UP\",\"ADDRS\":[{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAABQ==\"},{\"ADDRESSTYPE\":\"IFACE\",\"DATA\":\"AAAADw==\"}]}}";
+
+ CPIAddress *addr5 = cpiAddress_CreateFromInterface(5);
+ CPIAddress *addr15 = cpiAddress_CreateFromInterface(15);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Append(cpiAddressList_Create(), addr5), addr15);
+
+ CPIInterfaceEthernet *truth = cpiInterfaceEthernet_Create(1, list);
+ cpiInterfaceEthernet_SetState(truth, CPI_IFACE_UP);
+
+ PARCJSON *json = parcJSON_ParseString(truth_json_str);
+
+ CPIInterfaceEthernet *test = cpiInterfaceEthernet_CreateFromJson(json);
+ assertTrue(cpiInterfaceEthernet_Equals(truth, test), "Ethernet interfaces do not match");
+
+ parcJSON_Release(&json);
+ cpiInterfaceEthernet_Destroy(&truth);
+ cpiInterfaceEthernet_Destroy(&test);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_InterfaceEthernet);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceGeneric.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceGeneric.c
new file mode 100644
index 00000000..d427bb78
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceGeneric.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_InterfaceGeneric.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceGeneric)
+{
+ // 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(cpi_InterfaceGeneric)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_InterfaceGeneric)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_GetAddresses);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_GetIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_GetState);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceGeneric_BuildString);
+}
+
+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, cpiInterfaceGeneric_Copy)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceGeneric *Generic = cpiInterfaceGeneric_Create(1, list);
+
+ CPIInterfaceGeneric *copy = cpiInterfaceGeneric_Copy(Generic);
+
+ assertTrue(copy->ifidx == Generic->ifidx, "ifidx did not match, expected %u got %u", Generic->ifidx, copy->ifidx);
+ assertTrue(copy->state == Generic->state, "states did not match, expected %d got %d", Generic->state, copy->state);
+ assertTrue(cpiAddressList_Equals(copy->addresses, Generic->addresses), "did not get same addresses");
+
+ cpiInterfaceGeneric_Destroy(&copy);
+ cpiInterfaceGeneric_Destroy(&Generic);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceGeneric_Create_Destroy)
+{
+ CPIInterfaceGeneric *Generic = cpiInterfaceGeneric_Create(1, cpiAddressList_Create());
+ cpiInterfaceGeneric_Destroy(&Generic);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Imbalance after destroying");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceGeneric_GetAddresses)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceGeneric *Generic = cpiInterfaceGeneric_Create(1, list);
+
+ const CPIAddressList *test = cpiInterfaceGeneric_GetAddresses(Generic);
+ assertTrue(cpiAddressList_Equals(list, test), "Address lists did not match");
+
+ cpiInterfaceGeneric_Destroy(&Generic);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceGeneric_GetIndex)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceGeneric *Generic = cpiInterfaceGeneric_Create(1, list);
+
+ assertTrue(cpiInterfaceGeneric_GetIndex(Generic) == 1, "ifidx did not match");
+
+ cpiInterfaceGeneric_Destroy(&Generic);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceGeneric_GetState)
+{
+ CPIAddress *addr = cpiAddress_CreateFromInterface(5);
+ CPIAddressList *list = cpiAddressList_Append(cpiAddressList_Create(), addr);
+ CPIInterfaceGeneric *Generic = cpiInterfaceGeneric_Create(1, list);
+
+ assertTrue(cpiInterfaceGeneric_GetState(Generic) == CPI_IFACE_UNKNOWN, "state did not match");
+
+ cpiInterfaceGeneric_SetState(Generic, CPI_IFACE_UP);
+ assertTrue(cpiInterfaceGeneric_GetState(Generic) == CPI_IFACE_UP, "state did not match");
+
+ cpiInterfaceGeneric_SetState(Generic, CPI_IFACE_DOWN);
+ assertTrue(cpiInterfaceGeneric_GetState(Generic) == CPI_IFACE_DOWN, "state did not match");
+
+ cpiInterfaceGeneric_Destroy(&Generic);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceGeneric_BuildString)
+{
+ CPIAddressList *addrs = cpiAddressList_Create();
+ cpiAddressList_Append(addrs, cpiAddress_CreateFromInterface(1));
+ cpiAddressList_Append(addrs, cpiAddress_CreateFromInterface(2));
+
+ CPIInterfaceGeneric *generic = cpiInterfaceGeneric_Create(1, addrs);
+
+ uint32_t beforeBalance = parcMemory_Outstanding();
+ PARCBufferComposer *composer = cpiInterfaceGeneric_BuildString(generic, parcBufferComposer_Create());
+ parcBufferComposer_Release(&composer);
+ uint32_t afterBalance = parcMemory_Outstanding();
+
+ cpiInterfaceGeneric_Destroy(&generic);
+
+ assertTrue(beforeBalance == afterBalance, "Memory leak in BuildString: %d", (int) (afterBalance - beforeBalance));
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_InterfaceGeneric);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnel.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnel.c
new file mode 100644
index 00000000..20a5cfc7
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnel.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_InterfaceIPTunnel.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceIPTunnel)
+{
+ // 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(cpi_InterfaceIPTunnel)
+{
+ 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(cpi_InterfaceIPTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_GetAddresses);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_GetIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_GetState);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_ToJSON);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnel_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, cpiInterfaceIPTunnel_Copy)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+
+ CPIInterfaceIPTunnel *copy = cpiInterfaceIPTunnel_Copy(iptun);
+
+ assertTrue(cpiInterfaceIPTunnel_GetIndex(copy) == cpiInterfaceIPTunnel_GetIndex(iptun),
+ "ifidx did not match, expected %u got %u",
+ cpiInterfaceIPTunnel_GetIndex(iptun),
+ cpiInterfaceIPTunnel_GetIndex(copy));
+
+ assertTrue(cpiInterfaceIPTunnel_GetState(copy) == cpiInterfaceIPTunnel_GetState(iptun),
+ "states did not match, expected %d got %d",
+ cpiInterfaceIPTunnel_GetState(iptun),
+ cpiInterfaceIPTunnel_GetState(copy));
+
+ assertTrue(cpiAddress_Equals(cpiInterfaceIPTunnel_GetSourceAddress(copy), cpiInterfaceIPTunnel_GetSourceAddress(iptun)),
+ "did not get same source address");
+ assertTrue(cpiAddress_Equals(cpiInterfaceIPTunnel_GetDestinationAddress(copy), cpiInterfaceIPTunnel_GetDestinationAddress(iptun)),
+ "did not get same destination address");
+
+ assertTrue(cpiInterfaceIPTunnel_GetTunnelType(copy) == cpiInterfaceIPTunnel_GetTunnelType(iptun),
+ "did not get same tunnel types!");
+
+ assertNotNull(copy->symbolic, "Copy has null symbolic name");
+ assertTrue(strcmp(iptun->symbolic, copy->symbolic) == 0, "symbolics name wrong expected '%s' got '%s'",
+ iptun->symbolic, copy->symbolic);
+
+ cpiInterfaceIPTunnel_Release(&copy);
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_Create_Destroy)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_GRE, "tun0");
+ cpiInterfaceIPTunnel_Release(&iptun);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Imbalance after destroying");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_GetAddresses)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+
+ const CPIAddress *test;
+
+ test = cpiInterfaceIPTunnel_GetSourceAddress(iptun);
+ assertTrue(cpiAddress_Equals(src, test), "Address lists did not match");
+
+ test = cpiInterfaceIPTunnel_GetDestinationAddress(iptun);
+ assertTrue(cpiAddress_Equals(dst, test), "Address lists did not match");
+
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_GetIndex)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+
+ assertTrue(cpiInterfaceIPTunnel_GetIndex(iptun) == 1, "ifidx did not match");
+
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_GetState)
+{
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+
+ assertTrue(cpiInterfaceIPTunnel_GetState(iptun) == CPI_IFACE_UNKNOWN, "state did not match");
+
+ cpiInterfaceIPTunnel_SetState(iptun, CPI_IFACE_UP);
+ assertTrue(cpiInterfaceIPTunnel_GetState(iptun) == CPI_IFACE_UP, "state did not match");
+
+ cpiInterfaceIPTunnel_SetState(iptun, CPI_IFACE_DOWN);
+ assertTrue(cpiInterfaceIPTunnel_GetState(iptun) == CPI_IFACE_DOWN, "state did not match");
+
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_ToJSON)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char *expected = "{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAgHBgUAAAAAAAAAAA==\"}}}";
+#elif defined(__linux__)
+ char *expected = "{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAgHBgUAAAAAAAAAAA==\"}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+
+ PARCJSON *test_json = cpiInterfaceIPTunnel_ToJson(iptun);
+
+ char *actual = parcJSON_ToCompactString(test_json);
+ assertEqualStrings(expected, actual);
+
+ parcMemory_Deallocate((void **) &actual);
+ parcJSON_Release(&test_json);
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnel_FromJSON)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_json_str[] = "{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"STATE\":\"UP\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAgHBgUAAAAAAAAAAA==\"}}}";
+#elif defined(__linux__)
+ char truth_json_str[] = "{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"STATE\":\"UP\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAgHBgUAAAAAAAAAAA==\"}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+
+ CPIAddress *src = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 }));
+ CPIAddress *dst = cpiAddress_CreateFromInet(&((struct sockaddr_in) { .sin_addr.s_addr = 0x05060708 }));
+ CPIInterfaceIPTunnel *truth = cpiInterfaceIPTunnel_Create(1, src, dst, IPTUN_TCP, "tun0");
+ cpiInterfaceIPTunnel_SetState(truth, CPI_IFACE_UP);
+
+ PARCJSON *json = parcJSON_ParseString(truth_json_str);
+
+ CPIInterfaceIPTunnel *test = cpiInterfaceIPTunnel_CreateFromJson(json);
+ assertTrue(cpiInterfaceIPTunnel_Equals(truth, test), "IPTunnel interfaces do not match");
+
+ parcJSON_Release(&json);
+ cpiInterfaceIPTunnel_Release(&truth);
+ cpiInterfaceIPTunnel_Release(&test);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_InterfaceIPTunnel);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnelList.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnelList.c
new file mode 100644
index 00000000..956ac4c3
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceIPTunnelList.c
@@ -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.
+ */
+
+#include "../cpi_InterfaceIPTunnelList.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceIPTunnelList)
+{
+ // 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(cpi_InterfaceIPTunnelList)
+{
+ 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(cpi_InterfaceIPTunnelList)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnelList_Append);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnelList_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnelList_FromJson);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnelList_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceIPTunnelList_ToJson);
+}
+
+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 CPIInterfaceIPTunnel *
+createTunnelObject(unsigned ifidx, int s_addr, uint16_t s_port, int d_addr, uint16_t d_port)
+{
+ return cpiInterfaceIPTunnel_Create(ifidx,
+ cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_family = PF_INET, .sin_addr.s_addr = s_addr, .sin_port = s_port }),
+ cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_family = PF_INET, .sin_addr.s_addr = d_addr, .sin_port = d_port }),
+ IPTUN_TCP, "tun0");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnelList_Append)
+{
+ CPIInterfaceIPTunnelList *list = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(list, createTunnelObject(1, 2, 3, 4, 5));
+
+ assertTrue(parcArrayList_Size(list->listOfTunnels) == 1, "got wrong size, expected %u got %zu", 1, parcArrayList_Size(list->listOfTunnels));
+
+ cpiInterfaceIPTunnelList_Destroy(&list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnelList_Create_Destroy)
+{
+ CPIInterfaceIPTunnelList *list = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Destroy(&list);
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after create/destroy");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnelList_FromJson)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_string[] = "{\"TunnelList\":[{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIDAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#elif defined(__linux__)
+ char truth_string[] = "{\"TunnelList\":[{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgADAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+
+ CPIInterfaceIPTunnelList *truth_list = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(truth_list, createTunnelObject(1, 2, 3, 4, 5));
+
+ PARCJSON *truth_json = parcJSON_ParseString(truth_string);
+ CPIInterfaceIPTunnelList *test_list = cpiInterfaceIPTunnelList_FromJson(truth_json);
+
+ assertTrue(cpiInterfaceIPTunnelList_Equals(truth_list, test_list), "Lists do not match");
+
+ cpiInterfaceIPTunnelList_Destroy(&test_list);
+ parcJSON_Release(&truth_json);
+ cpiInterfaceIPTunnelList_Destroy(&truth_list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnelList_Equals)
+{
+ CPIInterfaceIPTunnelList *list_a = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(list_a, createTunnelObject(1, 2, 3, 4, 5));
+
+ CPIInterfaceIPTunnelList *list_b = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(list_b, createTunnelObject(1, 2, 3, 4, 5));
+
+ CPIInterfaceIPTunnelList *list_c = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(list_c, createTunnelObject(1, 2, 3, 4, 5));
+
+ CPIInterfaceIPTunnelList *unequal = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(unequal, createTunnelObject(99, 2, 3, 4, 5));
+ cpiInterfaceIPTunnelList_Append(unequal, createTunnelObject(1, 99, 3, 4, 5));
+ cpiInterfaceIPTunnelList_Append(unequal, createTunnelObject(1, 2, 99, 4, 5));
+ cpiInterfaceIPTunnelList_Append(unequal, createTunnelObject(1, 2, 3, 99, 5));
+ cpiInterfaceIPTunnelList_Append(unequal, createTunnelObject(1, 2, 3, 4, 99));
+
+ assertEqualsContract(cpiInterfaceIPTunnelList_Equals, list_a, list_b, list_c, unequal);
+
+ cpiInterfaceIPTunnelList_Destroy(&unequal);
+ cpiInterfaceIPTunnelList_Destroy(&list_a);
+ cpiInterfaceIPTunnelList_Destroy(&list_b);
+ cpiInterfaceIPTunnelList_Destroy(&list_c);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceIPTunnelList_ToJson)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth_string[] = "{\"TunnelList\":[{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIDAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#elif defined(__linux__)
+ char truth_string[] = "{\"TunnelList\":[{\"TUNNEL\":{\"IFIDX\":1,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgADAAIAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAFAAQAAAAAAAAAAAAAAA==\"}}}]}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CPIInterfaceIPTunnelList *list = cpiInterfaceIPTunnelList_Create();
+ cpiInterfaceIPTunnelList_Append(list, createTunnelObject(1, 2, 3, 4, 5));
+
+ PARCJSON *json = cpiInterfaceIPTunnelList_ToJson(list);
+ char *test = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(truth_string, test) == 0, "Got wrong JSON.\nexpected: %s\ngot %s\n", truth_string, test);
+ parcMemory_Deallocate((void **) &test);
+
+ parcJSON_Release(&json);
+ cpiInterfaceIPTunnelList_Destroy(&list);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_InterfaceIPTunnelList);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceSet.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceSet.c
new file mode 100644
index 00000000..d87149bf
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceSet.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_InterfaceSet.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceSet)
+{
+ // 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(cpi_InterfaceSet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_InterfaceSet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_Add_Single);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_Add_TwoUnique);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_Add_TwoSame);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_FromJson);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_GetByInterfaceIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_GetByName);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_GetByOrdinalIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_Length);
+ LONGBOW_RUN_TEST_CASE(Global, cpiInterfaceSet_ToJson);
+}
+
+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, cpiInterfaceSet_Add_Single)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface = cpiInterface_Create("eth0", 11, false, true, 1500);
+ bool success = cpiInterfaceSet_Add(set, iface);
+ assertTrue(success, "Adding one interface did not succeed");
+ assertTrue(parcArrayList_Size(set->listOfInterfaces) == 1, "List wrong size, expected %u got %zu", 1, parcArrayList_Size(set->listOfInterfaces));
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_Add_TwoUnique)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ bool success = cpiInterfaceSet_Add(set, iface0);
+ assertTrue(success, "Adding one interface did not succeed");
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ success = cpiInterfaceSet_Add(set, iface1);
+ assertTrue(success, "Adding second interface did not succeed");
+
+ assertTrue(parcArrayList_Size(set->listOfInterfaces) == 2, "List wrong size, expected %u got %zu", 2, parcArrayList_Size(set->listOfInterfaces));
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_Add_TwoSame)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ bool success = cpiInterfaceSet_Add(set, iface0);
+ assertTrue(success, "Adding one interface did not succeed");
+
+ CPIInterface *iface1 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ success = cpiInterfaceSet_Add(set, iface1);
+ assertFalse(success, "Adding second interface duplicate interface should have failed");
+ cpiInterface_Destroy(&iface1);
+
+ assertTrue(parcArrayList_Size(set->listOfInterfaces) == 1, "List wrong size, expected %u got %zu", 1, parcArrayList_Size(set->listOfInterfaces));
+ cpiInterfaceSet_Destroy(&set);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_Create_Destroy)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+ cpiInterfaceSet_Destroy(&set);
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after create/destroy");
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_GetByInterfaceIndex)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ CPIInterface *test = cpiInterfaceSet_GetByInterfaceIndex(set, 11);
+
+ assertTrue(cpiInterface_Equals(test, iface0), "Did not get back right interface");
+
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_GetByName)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ CPIInterface *test = cpiInterfaceSet_GetByName(set, "eth0");
+
+ assertTrue(cpiInterface_Equals(test, iface0), "Did not get back right interface");
+
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_GetByOrdinalIndex)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ CPIInterface *test = cpiInterfaceSet_GetByOrdinalIndex(set, 0);
+
+ assertTrue(cpiInterface_Equals(test, iface0), "Did not get back right interface");
+
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_Length)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ size_t length = cpiInterfaceSet_Length(set);
+
+ assertTrue(length == 2, "Wrong length, expected %u got %zu", 2, length);
+
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_ToJson)
+{
+ char truth[] = "{\"Interfaces\":[{\"Interface\":{\"Name\":\"eth0\",\"Index\":11,\"Loopback\":\"false\",\"Multicast\":\"true\",\"MTU\":1500,\"Addrs\":[]}},{\"Interface\":{\"Name\":\"eth1\",\"Index\":12,\"Loopback\":\"false\",\"Multicast\":\"true\",\"MTU\":1500,\"Addrs\":[]}}]}";
+
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ PARCJSON *json = cpiInterfaceSet_ToJson(set);
+ char *str = parcJSON_ToCompactString(json);
+ assertTrue(strcasecmp(truth, str) == 0, "Json wrong, expected '%s' got '%s'", truth, str);
+ parcMemory_Deallocate((void **) &str);
+
+ parcJSON_Release(&json);
+ cpiInterfaceSet_Destroy(&set);
+}
+
+LONGBOW_TEST_CASE(Global, cpiInterfaceSet_FromJson)
+{
+ CPIInterfaceSet *set = cpiInterfaceSet_Create();
+
+ CPIInterface *iface0 = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface0);
+
+ CPIInterface *iface1 = cpiInterface_Create("eth1", 12, false, true, 1500);
+ cpiInterfaceSet_Add(set, iface1);
+
+ PARCJSON *json = cpiInterfaceSet_ToJson(set);
+
+ CPIInterfaceSet *test_set = cpiInterfaceSet_FromJson(json);
+
+ assertTrue(cpiInterfaceSet_Equals(set, test_set), "CPIInterfaceSet from json did not equal truth set");
+
+ parcJSON_Release(&json);
+ cpiInterfaceSet_Destroy(&set);
+ cpiInterfaceSet_Destroy(&test_set);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_InterfaceSet);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceTypes.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceTypes.c
new file mode 100644
index 00000000..a207eecd
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_InterfaceTypes.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_InterfaceType.c"
+#include <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+
+
+LONGBOW_TEST_RUNNER(cpi_InterfaceTypes)
+{
+ // 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(cpi_InterfaceTypes)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_InterfaceTypes)
+{
+ 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)
+{
+ 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(cpi_InterfaceTypes);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Listener.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Listener.c
new file mode 100644
index 00000000..5bd5a534
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Listener.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_Listener.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+typedef struct test_data {
+ CPIListener *listener;
+
+ // the truth values of the connection
+ uint8_t macArray[6];
+ CPIAddress *macAddress;
+ uint16_t ethertype;
+ char ifname[16];
+ char symbolic[16];
+} TestData;
+
+static CPIListener *
+_conjureIPObject(CPIInterfaceIPTunnelType type, const char *addressString, uint16_t port, const char *symbolic)
+{
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ int result = inet_aton(addressString, &sin.sin_addr);
+ assertTrue(result == 1, "failed inet_aton: (%d) %s", errno, strerror(errno));
+
+ CPIAddress *address = cpiAddress_CreateFromInet(&sin);
+ CPIListener *listener = cpiListener_CreateIP(type, address, symbolic);
+ cpiAddress_Destroy(&address);
+
+ return listener;
+}
+
+LONGBOW_TEST_RUNNER(cpi_ConnectionEthernet)
+{
+ // 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(cpi_ConnectionEthernet)
+{
+ 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(cpi_ConnectionEthernet)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_CreateEther);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_CreateIP);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_Equals_Ether);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_Equals_IP);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_CreateAddMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_CreateRemoveMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsAddMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsRemoveMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_FromControl_Ether);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_FromControl_IP);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsEtherEncap);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsIPEncap);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_GetEtherType);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_GetInterfaceName);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_GetSymbolicName);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_GetAddress);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsProtocolUdp);
+ LONGBOW_RUN_TEST_CASE(Global, cpiListener_IsProtocolTcp);
+}
+
+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, cpiListener_CreateEther)
+{
+ CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ assertNotNull(listener, "Got null IP based listener");
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_CreateIP)
+{
+ CPIListener *listener = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ assertNotNull(listener, "Got null IP based listener");
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_Equals_Ether)
+{
+ CPIListener *x = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CPIListener *y = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CPIListener *z = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+
+ CPIListener *t = cpiListener_CreateEther("eth1", 0x0801, "puppy");
+ CPIListener *u = cpiListener_CreateEther("eth0", 0x0802, "puppy");
+ CPIListener *v = cpiListener_CreateEther("eth0", 0x0801, "kitten");
+
+ assertEqualsContract(cpiListener_Equals, x, y, z, t, u, v);
+
+ cpiListener_Release(&x);
+ cpiListener_Release(&y);
+ cpiListener_Release(&z);
+ cpiListener_Release(&t);
+ cpiListener_Release(&u);
+ cpiListener_Release(&v);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_Equals_IP)
+{
+ CPIListener *x = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ CPIListener *y = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ CPIListener *z = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+
+ CPIListener *t = _conjureIPObject(IPTUN_TCP, "127.0.0.1", 9596, "puppy");
+ CPIListener *u = _conjureIPObject(IPTUN_UDP, "127.0.2.1", 9596, "puppy");
+ CPIListener *v = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 1111, "puppy");
+ CPIListener *w = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "kitten");
+
+ assertEqualsContract(cpiListener_Equals, x, y, z, t, u, v, w);
+
+ cpiListener_Release(&x);
+ cpiListener_Release(&y);
+ cpiListener_Release(&z);
+ cpiListener_Release(&t);
+ cpiListener_Release(&u);
+ cpiListener_Release(&v);
+ cpiListener_Release(&w);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_CreateAddMessage)
+{
+ const char *truthFormat = "{\"CPI_REQUEST\":{\"SEQUENCE\":%d,\"%s\":{\"IFNAME\":\"eth0\",\"ETHERTYPE\":2049,\"SYMBOLIC\":\"puppy\"}}}";
+
+ char buffer[1024];
+
+ // Create the add message
+ CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ assertNotNull(control, "Got null control message");
+
+ // extract the sequence number to put in the truth string
+ PARCJSON *json = ccnxControl_GetJson(control);
+ uint64_t seqnum = controlPlaneInterface_GetSequenceNumber(json);
+ sprintf(buffer, truthFormat, (int) seqnum, KEY_ADDLISTENER);
+
+ char *testString = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(buffer, testString) == 0, "Got wrong JSON, expected\n%s\nGot\n%s\n", buffer, testString);
+ parcMemory_Deallocate((void **) &testString);
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_CreateRemoveMessage)
+{
+#if defined(__APPLE__)
+ const char *truthFormat = "{\"CPI_REQUEST\":{\"SEQUENCE\":%d,\"%s\":{\"IPROTO\":\"UDP\",\"ADDR\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIlfH8AAAEAAAAAAAAAAA==\"},\"SYMBOLIC\":\"puppy\"}}}";
+#elif defined(__linux__)
+ const char *truthFormat = "{\"CPI_REQUEST\":{\"SEQUENCE\":%d,\"%s\":{\"IPROTO\":\"UDP\",\"ADDR\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAlfH8AAAEAAAAAAAAAAA==\"},\"SYMBOLIC\":\"puppy\"}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ char buffer[1024];
+
+ // Create the remove message
+ CPIListener *listener = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ CCNxControl *control = cpiListener_CreateRemoveMessage(listener);
+ assertNotNull(control, "Got null control message");
+
+ // extract the sequence number to put in the truth string
+ PARCJSON *json = ccnxControl_GetJson(control);
+ uint64_t seqnum = controlPlaneInterface_GetSequenceNumber(json);
+ sprintf(buffer, truthFormat, (int) seqnum, KEY_REMOVELISTENER);
+
+ char *testString = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(buffer, testString) == 0, "Got wrong JSON, expected\n%s\nGot\n%s\n", buffer, testString);
+ parcMemory_Deallocate((void **) &testString);
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsAddMessage)
+{
+ CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+ assertNotNull(control, "Got null control message");
+
+ bool test = cpiListener_IsAddMessage(control);
+ assertTrue(test, "Add message denies it is one.");
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsRemoveMessage)
+{
+ CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CCNxControl *control = cpiListener_CreateRemoveMessage(listener);
+ assertNotNull(control, "Got null control message");
+
+ bool test = cpiListener_IsRemoveMessage(control);
+ assertTrue(test, "Add message denies it is one.");
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_FromControl_Ether)
+{
+ CPIListener *listener = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+
+ CPIListener *test = cpiListener_FromControl(control);
+ assertTrue(cpiListener_Equals(listener, test), "Listeners do not match")
+ {
+ printf("Expenected:\n");
+ char *str = parcJSON_ToString(ccnxControl_GetJson(control));
+ printf(" %s\n", str);
+ parcMemory_Deallocate((void **) &str);
+
+ printf("Got:\n");
+ CCNxControl *testControl = cpiListener_CreateAddMessage(test);
+ str = parcJSON_ToString(ccnxControl_GetJson(testControl));
+ printf(" %s\n", str);
+ parcMemory_Deallocate((void **) &str);
+ ccnxControl_Release(&testControl);
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&test);
+ cpiListener_Release(&listener);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiListener_FromControl_IP)
+{
+ CPIListener *listener = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ CCNxControl *control = cpiListener_CreateAddMessage(listener);
+
+ CPIListener *test = cpiListener_FromControl(control);
+ assertTrue(cpiListener_Equals(listener, test), "Listeners do not match")
+ {
+ printf("Expenected:\n");
+ char *str = parcJSON_ToString(ccnxControl_GetJson(control));
+ printf(" %s\n", str);
+ parcMemory_Deallocate((void **) &str);
+
+ printf("Got:\n");
+ CCNxControl *testControl = cpiListener_CreateAddMessage(test);
+ str = parcJSON_ToString(ccnxControl_GetJson(testControl));
+ printf(" %s\n", str);
+ parcMemory_Deallocate((void **) &str);
+ ccnxControl_Release(&testControl);
+ }
+
+ ccnxControl_Release(&control);
+ cpiListener_Release(&test);
+ cpiListener_Release(&listener);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsEtherEncap)
+{
+ CPIListener *x = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ bool isTrue = cpiListener_IsEtherEncap(x);
+ assertTrue(isTrue, "Ether listener says it is not ether");
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsIPEncap)
+{
+ CPIListener *x = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ bool isTrue = cpiListener_IsIPEncap(x);
+ assertTrue(isTrue, "IP listener says it is not IP");
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_GetAddress)
+{
+ CPIListener *x = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ CPIAddress *test = cpiListener_GetAddress(x);
+ assertNotNull(test, "Got null address for IP listener");
+
+ struct sockaddr_in sin;
+ cpiAddress_GetInet(test, &sin);
+ assertTrue(htons(sin.sin_port) == 9596, "Wrong port expected %u got %u", 9695, htons(sin.sin_port));
+
+ uint32_t testip = htonl(sin.sin_addr.s_addr);
+ uint32_t truthip = 0x7F000001;
+
+ assertTrue(testip == truthip, "Wrong IP address expected %#08x got %#08x", truthip, testip);
+
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_GetEtherType)
+{
+ CPIListener *x = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ uint16_t test = cpiListener_GetEtherType(x);
+ assertTrue(test == 0x0801, "Wrong ethertype, got %04x expected %04x", test, 0x0801);
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_GetInterfaceName)
+{
+ CPIListener *x = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ const char *test = cpiListener_GetInterfaceName(x);
+ assertTrue(strcmp(test, "eth0") == 0, "Wrong interface name, got '%s' expected '%s'", test, "eth0");
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_GetSymbolicName)
+{
+ CPIListener *x = cpiListener_CreateEther("eth0", 0x0801, "puppy");
+ const char *test = cpiListener_GetSymbolicName(x);
+ assertTrue(strcmp(test, "puppy") == 0, "Wrong symbolic name, got '%s' expected '%s'", test, "puppy");
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsProtocolUdp)
+{
+ CPIListener *x = _conjureIPObject(IPTUN_UDP, "127.0.0.1", 9596, "puppy");
+ assertTrue(cpiListener_IsProtocolUdp(x), "UDP listener did not say it was UDP");
+ cpiListener_Release(&x);
+}
+
+LONGBOW_TEST_CASE(Global, cpiListener_IsProtocolTcp)
+{
+ CPIListener *x = _conjureIPObject(IPTUN_TCP, "127.0.0.1", 9596, "puppy");
+ assertTrue(cpiListener_IsProtocolTcp(x), "TCP listener did not say it was TCP");
+ cpiListener_Release(&x);
+}
+
+// =========================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_ConnectionEthernet);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ManageLinks.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ManageLinks.c
new file mode 100644
index 00000000..381e49b1
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_ManageLinks.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_ManageLinks.c"
+#include <LongBow/testing.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+#include <parc/algol/parc_Network.h>
+#include <inttypes.h>
+
+
+
+static const short testCpiManageLinks_MetisPort = 9695;
+
+LONGBOW_TEST_RUNNER(cpi_ManageLinks)
+{
+ // 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(cpi_ManageLinks)
+{
+ 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(cpi_ManageLinks)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_CreateIPTunnel);
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_CreateInterfaceListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_InterfacesFromControlMessage);
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_InterfaceIPTunnelFromControlMessage);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_CreateConnectionListRequest);
+ LONGBOW_RUN_TEST_CASE(Global, cpiLinks_ConnectionListFromControlMessage);
+}
+
+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, cpiLinks_CreateIPTunnel)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform. Note that the port number is encoded in the JSON,
+ // so if you change the port the test will fail.
+#if defined(__APPLE__)
+ char *truth_format = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"CREATE_TUNNEL\":{\"TUNNEL\":{\"IFIDX\":0,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAAAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIl338AAAEAAAAAAAAAAA==\"}}}}}";
+#elif defined(__linux__)
+ char *truth_format = "{\"CPI_REQUEST\":{\"SEQUENCE\":%" PRIu64 ",\"CREATE_TUNNEL\":{\"TUNNEL\":{\"IFIDX\":0,\"SYMBOLIC\":\"tun0\",\"TUNTYPE\":\"TCP\",\"SRC\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAAAAAAAAAAAAAAAAA==\"},\"DST\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAl338AAAEAAAAAAAAAAA==\"}}}}}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(testCpiManageLinks_MetisPort);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIInterfaceIPTunnel *iptun = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP, "tun0");
+ CCNxControl *control = ccnxControl_CreateIPTunnelRequest(iptun);
+
+ char buffer[1024];
+ sprintf(buffer, truth_format, cpi_GetSequenceNumber(control));
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ char *test_string = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(buffer, test_string) == 0, "JSON strings did not match.\nexpected %s\ngot %s\n", buffer, test_string);
+ parcMemory_Deallocate((void **) &test_string);
+
+ ccnxControl_Release(&control);
+ cpiInterfaceIPTunnel_Release(&iptun);
+}
+
+LONGBOW_TEST_CASE(Global, cpiLinks_CreateInterfaceListRequest)
+{
+ char template[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%llu,\"INTERFACE_LIST\":{}}}";
+ char truth[1024];
+
+ CCNxControl *control = ccnxControl_CreateInterfaceListRequest();
+
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+
+ sprintf(truth, template, seqnum);
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ char *str = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(truth, str) == 0, "Did not get right json, expected '%s' got '%s'", truth, str);
+ parcMemory_Deallocate((void **) &str);
+
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiLinks_InterfacesFromControlMessage)
+{
+ CCNxControl *control = ccnxControl_CreateInterfaceListRequest();
+
+ CPIInterfaceSet *truth = cpiInterfaceSet_Create();
+ CPIInterface *iface = cpiInterface_Create("eth0", 11, false, true, 1500);
+ cpiInterfaceSet_Add(truth, iface);
+
+ PARCJSON *json = cpiInterfaceSet_ToJson(truth);
+ CCNxControl *response = cpi_CreateResponse(control, json);
+ parcJSON_Release(&json);
+ CPIInterfaceSet *test = cpiLinks_InterfacesFromControlMessage(response);
+
+ assertTrue(cpiInterfaceSet_Equals(test, truth), "Interface sets not equal");
+
+ ccnxControl_Release(&response);
+ cpiInterfaceSet_Destroy(&truth);
+ cpiInterfaceSet_Destroy(&test);
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiLinks_InterfaceIPTunnelFromControlMessage)
+{
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(testCpiManageLinks_MetisPort);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIInterfaceIPTunnel *truth = cpiInterfaceIPTunnel_Create(0, source, destination, IPTUN_TCP, "tun0");
+ CCNxControl *control = ccnxControl_CreateIPTunnelRequest(truth);
+
+ CPIInterfaceIPTunnel *test = cpiLinks_CreateIPTunnelFromControlMessage(control);
+
+ assertTrue(cpiInterfaceIPTunnel_Equals(truth, test), "InterfaceIPTunnels do not match");
+
+ ccnxControl_Release(&control);
+ cpiInterfaceIPTunnel_Release(&test);
+ cpiInterfaceIPTunnel_Release(&truth);
+}
+
+LONGBOW_TEST_CASE(Global, cpiLinks_CreateConnectionListRequest)
+{
+ char template[] = "{\"CPI_REQUEST\":{\"SEQUENCE\":%llu,\"CONNECTION_LIST\":{}}}";
+ char truth[1024];
+
+ CCNxControl *control = ccnxControl_CreateConnectionListRequest();
+ uint64_t seqnum = cpi_GetSequenceNumber(control);
+
+ sprintf(truth, template, seqnum);
+
+ PARCJSON *json = ccnxControl_GetJson(control);
+ char *str = parcJSON_ToCompactString(json);
+
+ assertTrue(strcmp(truth, str) == 0, "Did not get right json, expected '%s' got '%s'", truth, str);
+ parcMemory_Deallocate((void **) &str);
+
+ ccnxControl_Release(&control);
+}
+
+LONGBOW_TEST_CASE(Global, cpiLinks_ConnectionListFromControlMessage)
+{
+ // The request we'll create a response to
+ CCNxControl *request = ccnxControl_CreateConnectionListRequest();
+
+ // ---------------------------
+ // Tunnel addresses
+ struct sockaddr_in sockaddr_any;
+ memset(&sockaddr_any, 0, sizeof(sockaddr_any));
+ sockaddr_any.sin_family = PF_INET;
+ sockaddr_any.sin_addr.s_addr = INADDR_ANY;
+
+ CPIAddress *source = cpiAddress_CreateFromInet(&sockaddr_any);
+
+ struct sockaddr_in sockaddr_dst;
+ memset(&sockaddr_dst, 0, sizeof(sockaddr_dst));
+ sockaddr_dst.sin_family = PF_INET;
+ sockaddr_dst.sin_port = htons(testCpiManageLinks_MetisPort);
+ inet_pton(AF_INET, "127.0.0.1", &(sockaddr_dst.sin_addr));
+
+ CPIAddress *destination = cpiAddress_CreateFromInet(&sockaddr_dst);
+
+ // ---------------------------
+
+ CPIConnectionList *truth_list = cpiConnectionList_Create();
+ cpiConnectionList_Append(truth_list, cpiConnection_Create(0, source, destination, cpiConnection_TCP));
+
+ PARCJSON *json = cpiConnectionList_ToJson(truth_list);
+ CCNxControl *response = cpi_CreateResponse(request, json);
+ parcJSON_Release(&json);
+ CPIConnectionList *test = cpiLinks_ConnectionListFromControlMessage(response);
+
+ assertTrue(cpiConnectionList_Equals(truth_list, test), "InterfaceIPTunnels do not match");
+
+ ccnxControl_Release(&response);
+ ccnxControl_Release(&request);
+ cpiConnectionList_Destroy(&test);
+ cpiConnectionList_Destroy(&truth_list);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_ManageLinks);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_NameRouteType.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_NameRouteType.c
new file mode 100644
index 00000000..85175a7e
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_NameRouteType.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_NameRouteType.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(cpi_NameRouteType)
+{
+ // 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(cpi_NameRouteType)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_NameRouteType)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiNameRouteType_ToString);
+ LONGBOW_RUN_TEST_CASE(Global, cpiNameRouteType_FromString);
+}
+
+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, cpiNameRouteType_ToString)
+{
+ int numEntries = sizeof(nameRouteTypeString) / sizeof(nameRouteTypeString[0]);
+
+ for (int i = 0; i < numEntries; i++) {
+ if (nameRouteTypeString[i].type != 0) {
+ assertTrue(cpiNameRouteType_ToString(nameRouteTypeString[i].type) != NULL, "Expected non-NULL type name");
+ }
+ }
+}
+
+LONGBOW_TEST_CASE(Global, cpiNameRouteType_FromString)
+{
+ int numEntries = sizeof(nameRouteTypeString) / sizeof(nameRouteTypeString[0]);
+
+ for (int i = 0; i < numEntries; i++) {
+ if (nameRouteTypeString[i].str != NULL) {
+ CPINameRouteType type = cpiNameRouteType_FromString(nameRouteTypeString[i].str);
+ assertTrue(type == cpiNameRouteType_DEFAULT || type == cpiNameRouteType_EXACT_MATCH || type == cpiNameRouteType_LONGEST_MATCH, "unexpected route type");
+ }
+ }
+ // Test a name that doesn't exist. (need to catch the trap) Case 1034
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_NameRouteType);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Registration.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Registration.c
new file mode 100644
index 00000000..6f15c0e0
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_Registration.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 <LongBow/unit-test.h>
+
+#include <parc/algol/parc_SafeMemory.h>
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../cpi_NameRouteProtocolType.c"
+
+
+LONGBOW_TEST_RUNNER(cpi_NameRouteProtocolType)
+{
+ // 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(cpi_NameRouteProtocolType)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_NameRouteProtocolType)
+{
+ 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;
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_NameRouteProtocolType);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntry.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntry.c
new file mode 100644
index 00000000..97b85ead
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntry.c
@@ -0,0 +1,547 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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/unit-test.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <LongBow/unit-test.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../cpi_RouteEntry.c"
+
+LONGBOW_TEST_RUNNER(cpi_RouteEntry)
+{
+ // 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(Getters);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(cpi_RouteEntry)
+{
+ 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(cpi_RouteEntry)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_Copy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_CreateSymbolic);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_ToJson_1);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_ToJson_2);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_ToJson_3);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_ToJson_4);
+
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_FromJson_1);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_FromJson_2);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_FromJson_3);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_FromJson_4);
+}
+
+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, cpiRouteEntry_Create_Destroy)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ unsigned cost = 4;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ cpiRouteEntry_Destroy(&route);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance on create/destroy: %u", parcMemory_Outstanding());
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_Copy)
+{
+ CCNxName *prefix_a = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *a = cpiRouteEntry_Create(prefix_a, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ CPIRouteEntry *b = cpiRouteEntry_Copy(a);
+
+ assertTrue(cpiRouteEntry_Equals(a, b), "Copy did not compare as equals");
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&a);
+ cpiRouteEntry_Destroy(&b);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_Equals)
+{
+ CCNxName *prefix_a = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ CCNxName *prefix_b = ccnxName_Copy(prefix_a);
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *a = cpiRouteEntry_Create(prefix_a, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ CPIRouteEntry *b = cpiRouteEntry_Create(prefix_b, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ assertTrue(cpiRouteEntry_Equals(a, b), "Equals did not compare correctly");
+
+ cpiRouteEntry_Destroy(&a);
+ cpiRouteEntry_Destroy(&b);
+ cpiAddress_Destroy(&nexthop);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_CreateSymbolic)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned cost = 4;
+
+ CPIRouteEntry *route = cpiRouteEntry_CreateSymbolic(prefix, "tun0", cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ cpiRouteEntry_Destroy(&route);
+
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance on create/destroy: %u", parcMemory_Outstanding());
+}
+
+
+/**
+ * Add route with all options
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_ToJson_1)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200,\"LIFETIME\":[3600,0]}";
+#elif defined(__linux__)
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200,\"LIFETIME\":[3600,0]}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ PARCJSON *test_json = cpiRouteEntry_ToJson(route);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Route json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ cpiRouteEntry_Destroy(&route);
+ cpiAddress_Destroy(&nexthop);
+ parcJSON_Release(&test_json);
+}
+
+/**
+ * Add route without lifeitme
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_ToJson_2)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+#if defined(__APPLE__)
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AAIAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}";
+#elif defined(__linux__)
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"NEXTHOP\":{\"ADDRESSTYPE\":\"INET\",\"DATA\":\"AgAAAAQDAgEAAAAAAAAAAA==\"},\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}";
+#else
+ // Case 1033
+ testUnimplemented("Platform not supported");
+ return;
+#endif
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+ PARCJSON *test_json = cpiRouteEntry_ToJson(route);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Route json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ cpiRouteEntry_Destroy(&route);
+ cpiAddress_Destroy(&nexthop);
+ parcJSON_Release(&test_json);
+}
+
+/**
+ * Add route without lifeitme or nexthop
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_ToJson_3)
+{
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200}";
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+
+ PARCJSON *test_json = cpiRouteEntry_ToJson(route);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Control message json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ cpiRouteEntry_Destroy(&route);
+ parcJSON_Release(&test_json);
+}
+
+/**
+ * Add route with symbolic name
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_ToJson_4)
+{
+ // The JSON representation depends on the system sockaddr_in format, which
+ // varies platform to platform.
+ char truth[] = "{\"PREFIX\":\"ccnx:/howdie/stranger\",\"SYMBOLIC\":\"tun0\",\"INTERFACE\":55,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":200,\"LIFETIME\":[3600,0]}";
+
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_CreateSymbolic(prefix, "tun0", cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ cpiRouteEntry_SetInterfaceIndex(route, ifidx);
+
+ PARCJSON *test_json = cpiRouteEntry_ToJson(route);
+ char *test = parcJSON_ToCompactString(test_json);
+ assertTrue(strcasecmp(truth, test) == 0, "Route json does not match, expected '%s', got '%s'", truth, test);
+ parcMemory_Deallocate((void **) &test);
+
+ cpiRouteEntry_Destroy(&route);
+ parcJSON_Release(&test_json);
+}
+
+/**
+ * Add route with all options
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_FromJson_1)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route_truth = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ PARCJSON *truth_json = cpiRouteEntry_ToJson(route_truth);
+
+ CPIRouteEntry *route_test = cpiRouteEntry_FromJson(truth_json);
+ assertTrue(cpiRouteEntry_Equals(route_truth, route_test), "FromJson does not match");
+
+
+ cpiRouteEntry_Destroy(&route_truth);
+ cpiRouteEntry_Destroy(&route_test);
+ cpiAddress_Destroy(&nexthop);
+ parcJSON_Release(&truth_json);
+}
+
+/**
+ * Add route without lifeitme
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_FromJson_2)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ unsigned cost = 200;
+
+ CPIRouteEntry *route_truth = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+
+ PARCJSON *truth_json = cpiRouteEntry_ToJson(route_truth);
+
+ CPIRouteEntry *route_test = cpiRouteEntry_FromJson(truth_json);
+ assertTrue(cpiRouteEntry_Equals(route_truth, route_test), "FromJson does not match");
+
+
+ cpiRouteEntry_Destroy(&route_truth);
+ cpiRouteEntry_Destroy(&route_test);
+ cpiAddress_Destroy(&nexthop);
+ parcJSON_Release(&truth_json);
+}
+
+/**
+ * Add route without lifeitme or nexthop
+ */
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_FromJson_3)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ unsigned cost = 200;
+
+ CPIRouteEntry *route_truth = cpiRouteEntry_Create(prefix, ifidx, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, cost);
+
+ PARCJSON *truth_json = cpiRouteEntry_ToJson(route_truth);
+
+ CPIRouteEntry *route_test = cpiRouteEntry_FromJson(truth_json);
+ assertTrue(cpiRouteEntry_Equals(route_truth, route_test), "FromJson does not match");
+
+
+ cpiRouteEntry_Destroy(&route_truth);
+ cpiRouteEntry_Destroy(&route_test);
+ parcJSON_Release(&truth_json);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_FromJson_4)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route_truth = cpiRouteEntry_CreateSymbolic(prefix, "tun0", cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+ cpiRouteEntry_SetInterfaceIndex(route_truth, ifidx);
+
+ PARCJSON *truth_json = cpiRouteEntry_ToJson(route_truth);
+
+ CPIRouteEntry *route_test = cpiRouteEntry_FromJson(truth_json);
+ assertTrue(cpiRouteEntry_Equals(route_truth, route_test), "FromJson does not match");
+
+ const char *symbolic = cpiRouteEntry_GetSymbolicName(route_test);
+ assertTrue(strcmp(symbolic, "tun0") == 0, "wrong symbolic name expected 'tun0' got '%s'", symbolic);
+
+ cpiRouteEntry_Destroy(&route_truth);
+ cpiRouteEntry_Destroy(&route_test);
+ parcJSON_Release(&truth_json);
+}
+
+
+// ====================================================
+
+LONGBOW_TEST_FIXTURE(Getters)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetCost);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetInterfaceIndex);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetLifetime);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetNexthop);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetPrefix);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetRouteProtocolType);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetRouteType);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntry_GetSymbolicName);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Getters)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Getters)
+{
+ 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, cpiRouteEntry_GetCost)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ assertTrue(cpiRouteEntry_GetCost(route) == cost, "Got wrong cost, expected %u got %u", cost, cpiRouteEntry_GetCost(route));
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetInterfaceIndex)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ assertTrue(cpiRouteEntry_GetInterfaceIndex(route) == ifidx, "Got wrong cost, expected %u got %u", ifidx, cpiRouteEntry_GetInterfaceIndex(route));
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetLifetime)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ struct timeval test_time = cpiRouteEntry_GetLifetime(route);
+
+ assertTrue(lifetime.tv_sec == test_time.tv_sec && lifetime.tv_usec == test_time.tv_usec,
+ "Got wrong lifetime, expected %.6f got %.6f",
+ lifetime.tv_sec + 1E-6 * lifetime.tv_usec,
+ test_time.tv_sec + 1E-6 * test_time.tv_usec);
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetNexthop)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ const CPIAddress *test = cpiRouteEntry_GetNexthop(route);
+
+ assertTrue(cpiAddress_Equals(nexthop, test),
+ "Got wrong nexthop, expected %s got %s",
+ cpiAddress_ToString(nexthop),
+ cpiAddress_ToString(test));
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetPrefix)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ const CCNxName *test_prefix = cpiRouteEntry_GetPrefix(route);
+
+ assertTrue(ccnxName_Equals(prefix, test_prefix),
+ "Got wrong name, expected %s got %s",
+ ccnxName_ToString(prefix),
+ ccnxName_ToString(test_prefix));
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetRouteProtocolType)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ assertTrue(cpiRouteEntry_GetRouteProtocolType(route) == cpiNameRouteProtocolType_STATIC,
+ "Got wrong protocol, expected %d got %d",
+ cpiNameRouteProtocolType_STATIC,
+ cpiRouteEntry_GetRouteProtocolType(route));
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetRouteType)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ unsigned ifidx = 55;
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+
+ CPIRouteEntry *route = cpiRouteEntry_Create(prefix, ifidx, nexthop, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ assertTrue(cpiRouteEntry_GetRouteType(route) == cpiNameRouteType_LONGEST_MATCH,
+ "Got wrong route type, expected %d got %d",
+ cpiNameRouteType_LONGEST_MATCH,
+ cpiRouteEntry_GetRouteType(route));
+
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntry_GetSymbolicName)
+{
+ CCNxName *prefix = ccnxName_CreateFromCString("lci:/howdie/stranger");
+ CPIAddress *nexthop = cpiAddress_CreateFromInet(&(struct sockaddr_in) { .sin_addr.s_addr = 0x01020304 });
+ struct timeval lifetime = { 3600, 0 };
+ unsigned cost = 200;
+ const char *symbolicName = "tun0";
+
+ CPIRouteEntry *route = cpiRouteEntry_CreateSymbolic(prefix, symbolicName, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, &lifetime, cost);
+
+ const char *test = cpiRouteEntry_GetSymbolicName(route);
+
+ assertTrue(strcmp(symbolicName, test) == 0, "Got wrong symbolic name, expected %s got %s", symbolicName, test);
+ cpiAddress_Destroy(&nexthop);
+ cpiRouteEntry_Destroy(&route);
+}
+
+// =============================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_RouteEntry);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntryList.c b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntryList.c
new file mode 100644
index 00000000..94f2b473
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/control/test/test_cpi_RouteEntryList.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "../cpi_RouteEntryList.c"
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+
+LONGBOW_TEST_RUNNER(cpi_RouteEntryList)
+{
+ // 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(cpi_RouteEntryList)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(cpi_RouteEntryList)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_Append);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_Create_Destroy);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_FromJson);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_FromJson_EmptyList);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_Equals);
+ LONGBOW_RUN_TEST_CASE(Global, cpiRouteEntryList_ToJson);
+}
+
+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;
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_Append)
+{
+ CPIRouteEntryList *list = cpiRouteEntryList_Create();
+
+ CPIRouteEntry *entry = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(list, entry);
+
+ assertTrue(parcArrayList_Size(list->listOfRouteEntries) == 1, "got wrong size, expected %u got %zu", 1, parcArrayList_Size(list->listOfRouteEntries));
+
+ cpiRouteEntryList_Destroy(&list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_Create_Destroy)
+{
+ CPIRouteEntryList *list = cpiRouteEntryList_Create();
+ cpiRouteEntryList_Destroy(&list);
+ assertTrue(parcMemory_Outstanding() == 0, "Memory imbalance after create/destroy");
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_FromJson)
+{
+ char truth_string[] = "{\"Routes\":[{\"PREFIX\":\"ccnx:/hello\",\"INTERFACE\":1,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":1}]}";
+
+ CPIRouteEntryList *truth_list = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(truth_list, entry);
+
+ PARCJSON *truth_json = parcJSON_ParseString(truth_string);
+ CPIRouteEntryList *test_list = cpiRouteEntryList_FromJson(truth_json);
+
+ assertTrue(cpiRouteEntryList_Equals(truth_list, test_list), "Lists do not match");
+
+ cpiRouteEntryList_Destroy(&test_list);
+ parcJSON_Release(&truth_json);
+ cpiRouteEntryList_Destroy(&truth_list);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_FromJson_EmptyList)
+{
+ char truth_string[] = "{\"Routes\":[]}";
+
+ CPIRouteEntryList *truth_list = cpiRouteEntryList_Create();
+
+ PARCJSON *truth_json = parcJSON_ParseString(truth_string);
+ CPIRouteEntryList *test_list = cpiRouteEntryList_FromJson(truth_json);
+
+ assertTrue(cpiRouteEntryList_Equals(truth_list, test_list), "Lists do not match");
+
+ cpiRouteEntryList_Destroy(&test_list);
+ parcJSON_Release(&truth_json);
+ cpiRouteEntryList_Destroy(&truth_list);
+}
+
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_Equals)
+{
+ CPIRouteEntryList *list_a = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry_a = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(list_a, entry_a);
+
+ CPIRouteEntryList *list_b = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry_b = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(list_b, entry_b);
+
+ CPIRouteEntryList *list_c = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry_c = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(list_c, entry_c);
+
+ CPIRouteEntryList *unequal_length = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry_d = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ CPIRouteEntry *entry_e = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(unequal_length, entry_d);
+ cpiRouteEntryList_Append(unequal_length, entry_e);
+
+ CPIRouteEntryList *unequal_value = cpiRouteEntryList_Create();
+ CPIRouteEntry *entry_f = cpiRouteEntry_Create(ccnxName_CreateFromCString("ccnx:/hello"), 2, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(unequal_value, entry_f);
+
+ assertEqualsContract(cpiRouteEntryList_Equals, list_a, list_b, list_c, unequal_length, unequal_value);
+
+ cpiRouteEntryList_Destroy(&unequal_value);
+ cpiRouteEntryList_Destroy(&unequal_length);
+
+ cpiRouteEntryList_Destroy(&list_a);
+ cpiRouteEntryList_Destroy(&list_b);
+ cpiRouteEntryList_Destroy(&list_c);
+}
+
+LONGBOW_TEST_CASE(Global, cpiRouteEntryList_ToJson)
+{
+ char truth_string[] = "{\"Routes\":[{\"PREFIX\":\"ccnx:/hello\",\"INTERFACE\":1,\"FLAGS\":0,\"PROTOCOL\":\"STATIC\",\"ROUTETYPE\":\"LONGEST\",\"COST\":1}]}";
+
+ CPIRouteEntryList *list = cpiRouteEntryList_Create();
+
+ CPIRouteEntry *entry = cpiRouteEntry_Create(ccnxName_CreateFromCString("lci:/hello"), 1, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ cpiRouteEntryList_Append(list, entry);
+
+ PARCJSON *json = cpiRouteEntryList_ToJson(list);
+ char *test = parcJSON_ToCompactString(json);
+ assertTrue(strcmp(truth_string, test) == 0, "Got wrong JSON.\nexpected: %s\ngot %s\n", truth_string, test);
+ parcMemory_Deallocate((void **) &test);
+
+ parcJSON_Release(&json);
+ cpiRouteEntryList_Destroy(&list);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(cpi_RouteEntryList);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/api/notify/CMakeLists.txt b/libccnx-transport-rta/ccnx/api/notify/CMakeLists.txt
new file mode 100644
index 00000000..d22e4db3
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Define a few configuration variables that we want accessible in the software
+
+set(CCNX_API_NOTIFY_HEADERS
+ ccnxNotifyAPI_About.h
+ notify_Status.h
+ notify_Timer.h
+)
+
+set(CCNX_API_NOTIFY_SOURCE_FILES
+ ccnxNotifyAPI_About.c
+ notify_Status.c
+)
+
+
+add_library(ccnx_api_notify STATIC ${CCNX_API_NOTIFY_SOURCE_FILES} ${CCNX_API_NOTIFY_HEADERS})
+add_library(ccnx_api_notify.shared SHARED ${CCNX_API_NOTIFY_SOURCE_FILES})
+
+source_group(Sources FILES ${CCNX_API_NOTIFY_SOURCE_FILES})
+source_group(Sources FILES ${CCNX_API_NOTIFY_HEADERS})
+
+set_target_properties(ccnx_api_notify.shared PROPERTIES
+ C_STANDARD 99
+ SOVERSION 1
+ VERSION 1.0
+ OUTPUT_NAME ccnx_api_notify )
+
+set(libccnx_api_notify_libraries
+ ccnx_api_notify
+ ccnx_api_notify.shared
+ )
+
+foreach(lib ${libccnx_api_notify_libraries})
+ install(TARGETS ${lib} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
+ set_property(TARGET ${lib} PROPERTY C_STANDARD 99)
+endforeach()
+
+install(FILES ${CCNX_API_NOTIFY_HEADERS} DESTINATION include/ccnx/api/notify )
+
+#add_subdirectory(test)
diff --git a/libccnx-transport-rta/ccnx/api/notify/README b/libccnx-transport-rta/ccnx/api/notify/README
new file mode 100644
index 00000000..a2eb8879
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/README
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+API to handle notifications from the Transport. These notifications
+are specific to the RTA Transport, in that they use the Component
+model and Component names.
+
diff --git a/libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_About.c b/libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_About.c
new file mode 100644
index 00000000..25772c22
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_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 "ccnxNotifyAPI_About.h"
+
+const char *ccnxNotifyAPI_What = "@(#)" "ccnxNotifyAPI " RELEASE_VERSION " 2017-02-20T14:20:21.238467"
+ "@(#)" "\tCopyright (c) 2017 Cisco and/or its affiliates.";
+
+const char *
+ccnxNotifyAPIAbout_Name(void)
+{
+ return "ccnxNotifyAPI";
+}
+
+const char *
+ccnxNotifyAPIAbout_Version(void)
+{
+ return RELEASE_VERSION;
+}
+
+const char *
+ccnxNotifyAPIAbout_About(void)
+{
+ return "ccnxNotifyAPI "RELEASE_VERSION " 2017-02-20T14:20:21.238467" "\nCopyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxNotifyAPIAbout_MiniNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxNotifyAPIAbout_ShortNotice(void)
+{
+ return "Copyright (c) 2017 Cisco and/or its affiliates.\n";
+}
+
+const char *
+ccnxNotifyAPIAbout_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/api/notify/ccnxNotifyAPI_About.h b/libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_About.h
new file mode 100644
index 00000000..07d9ff50
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/ccnxNotifyAPI_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 ccnxNotifyAPI_About_h
+#define ccnxNotifyAPI_About_h
+/**
+ * Embedded string containing information for the what(1) command.
+ *
+ */
+extern const char *ccnxNotifyAPI_What;
+
+/**
+ * Return the name as a C string.
+ *
+ * @return The name as a C string.
+ */
+const char *ccnxNotifyAPIAbout_Name(void);
+
+/**
+ * Return the version as a C string.
+ *
+ * @return The version as a C string.
+ */
+const char *ccnxNotifyAPIAbout_Version(void);
+
+/**
+ * Return the About text as a C string.
+ *
+ * @return The About text as a C string.
+ */
+const char *ccnxNotifyAPIAbout_About(void);
+
+/**
+ * Return the minimum copyright notice as a C string.
+ *
+ * @return The minimum copyright notice as a C string.
+ */
+const char *ccnxNotifyAPIAbout_MiniNotice(void);
+
+/**
+ * Return the short copyright notice as a C string.
+ *
+ * @return The short copyright notice as a C string.
+ */
+const char *ccnxNotifyAPIAbout_ShortNotice(void);
+
+/**
+ * Return the long copyright notice as a C string.
+ *
+ * @return The long copyright notice as a C string.
+ */
+const char *ccnxNotifyAPIAbout_LongNotice(void);
+
+#endif // ccnxNotifyAPI_About_h
diff --git a/libccnx-transport-rta/ccnx/api/notify/notify_Status.c b/libccnx-transport-rta/ccnx/api/notify/notify_Status.c
new file mode 100644
index 00000000..25ade9fb
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/notify_Status.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 <config.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <parc/algol/parc_DisplayIndented.h>
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Memory.h>
+#include <parc/algol/parc_JSON.h>
+
+#include <ccnx/api/notify/notify_Status.h>
+
+// These string constants are used in the JSON configuration
+
+static const char jsonNotifyStatus[] = "notifyStatus";
+static const char param_CONNECTION[] = "connectionId";
+static const char param_CODE[] = "statusCode";
+static const char param_NAME[] = "name";
+static const char param_MESSAGE[] = "message";
+
+struct notify_status {
+ int apiFd;
+ NotifyStatusCode code;
+ CCNxName *name;
+ char *message;
+};
+
+static void
+_notifyStatus_Destroy(NotifyStatus **notifyStatusPtr)
+{
+ NotifyStatus *status = *notifyStatusPtr;
+ if (status->name) {
+ ccnxName_Release(&status->name);
+ }
+ if (status->message) {
+ parcMemory_Deallocate((void **) &status->message);
+ }
+}
+
+parcObject_ExtendPARCObject(NotifyStatus, _notifyStatus_Destroy, NULL, NULL, notifyStatus_Equals, NULL, NULL, NULL);
+
+parcObject_ImplementAcquire(notifyStatus, NotifyStatus);
+
+parcObject_ImplementRelease(notifyStatus, NotifyStatus);
+
+NotifyStatus *
+notifyStatus_Create(int apiFd, NotifyStatusCode code, CCNxName *name, const char *message)
+{
+ NotifyStatus *result = parcObject_CreateInstance(NotifyStatus);
+ result->apiFd = apiFd;
+ result->code = code;
+ result->name = name == NULL ? NULL : ccnxName_Acquire(name);
+ result->message = message == NULL ? NULL : parcMemory_StringDuplicate(message, strlen(message));
+
+ return result;
+}
+
+static bool
+_StringEquals(const char *x, const char *y)
+{
+ if (x == y) {
+ return true;
+ }
+ if (x == NULL || y == NULL) {
+ return false;
+ }
+ return strcmp(x, y) == 0;
+}
+
+bool
+notifyStatus_Equals(const NotifyStatus *x, const NotifyStatus *y)
+{
+ if (x == y) {
+ return true;
+ }
+ if (x == NULL || y == NULL) {
+ return false;
+ }
+
+ if (x->apiFd == y->apiFd) {
+ if (x->code == y->code) {
+ if (_StringEquals(x->message, y->message)) {
+ if (ccnxName_Equals(x->name, y->name)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+int
+notifyStatus_GetFiledes(const NotifyStatus *status)
+{
+ return status->apiFd;
+}
+
+NotifyStatusCode
+notifyStatus_GetStatusCode(const NotifyStatus *status)
+{
+ return status->code;
+}
+
+CCNxName *
+notifyStatus_GetName(const NotifyStatus *status)
+{
+ return status->name;
+}
+
+char *
+notifyStatus_GetMessage(const NotifyStatus *status)
+{
+ return status->message;
+}
+
+void
+notifyStatus_Display(const NotifyStatus *status, int indentation)
+{
+ parcDisplayIndented_PrintLine(indentation, "NotifyStatus%p { .apiFd=%d, .code=%d ", status, status->apiFd, status->code);
+ ccnxName_Display(status->name, indentation + 1);
+ parcDisplayIndented_PrintLine(indentation, ".message=\"%s\"\n", status->message);
+}
+
+bool
+notifyStatus_IsConnectionOpen(const NotifyStatus *status)
+{
+ return status->code == notifyStatusCode_CONNECTION_OPEN;
+}
+
+bool
+notifyStatus_IsFlowControlStarted(const NotifyStatus *status)
+{
+ return status->code == notifyStatusCode_FLOW_CONTROL_STARTED;
+}
+
+NotifyStatus *
+notifyStatus_ParseJSON(const PARCJSON *json)
+{
+ NotifyStatus *result = NULL;
+
+ PARCJSONValue *status = parcJSON_GetValueByName(json, jsonNotifyStatus);
+ if (status != NULL) {
+ PARCJSON *status_json = parcJSONValue_GetJSON(status);
+ int apiFd = (int) parcJSONValue_GetInteger(parcJSON_GetValueByName(status_json, param_CONNECTION));
+ NotifyStatusCode code =
+ (NotifyStatusCode) parcJSONValue_GetInteger(parcJSON_GetValueByName(status_json, param_CODE));
+
+ CCNxName *name = NULL;
+
+ PARCJSONValue *nameValue = parcJSON_GetValueByName(status_json, param_NAME);
+ if (nameValue != NULL) {
+ PARCBuffer *sBuf = parcJSONValue_GetString(nameValue);
+ const char *p = parcBuffer_Overlay(sBuf, 0);
+ if (p != NULL) {
+ name = ccnxName_CreateFromCString(p);
+ }
+ }
+
+ const char *message = NULL;
+ PARCJSONValue *messageValue = parcJSON_GetValueByName(status_json, param_MESSAGE);
+ if (messageValue != NULL) {
+ PARCBuffer *sBuf = parcJSONValue_GetString(messageValue);
+ message = parcBuffer_Overlay(sBuf, 0);
+ }
+
+ result = notifyStatus_Create(apiFd, code, name, message);
+ if (name != NULL) {
+ ccnxName_Release(&name);
+ }
+ }
+ return result;
+}
+
+PARCJSON *
+notifyStatus_ToJSON(const NotifyStatus *status)
+{
+ PARCJSON *json = parcJSON_Create();
+
+ parcJSON_AddInteger(json, param_CONNECTION, notifyStatus_GetFiledes(status));
+ parcJSON_AddInteger(json, param_CODE, notifyStatus_GetStatusCode(status));
+
+ if (notifyStatus_GetName(status) != NULL) {
+ char *nameAsString = ccnxName_ToString(notifyStatus_GetName(status));
+ parcJSON_AddString(json, param_NAME, nameAsString);
+ parcMemory_Deallocate((void **) &nameAsString);
+ }
+ if (notifyStatus_GetMessage(status) != NULL) {
+ parcJSON_AddString(json, param_MESSAGE, notifyStatus_GetMessage(status));
+ }
+
+ PARCJSON *result = parcJSON_Create();
+ parcJSON_AddObject(result, jsonNotifyStatus, json);
+ parcJSON_Release(&json);
+
+ return result;
+}
diff --git a/libccnx-transport-rta/ccnx/api/notify/notify_Status.h b/libccnx-transport-rta/ccnx/api/notify/notify_Status.h
new file mode 100644
index 00000000..3fcdcb1d
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/notify_Status.h
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 notify_Status.h
+ *
+ * @brief
+ * An API to handle notifications from the Transport.
+ *
+ * These notifications are specific to the RTA Transport, in that they use the Component model and Component names.
+ *
+ */
+#ifndef Libccnx_notifyStatus_h
+#define Libccnx_notifyStatus_h
+
+#include <parc/algol/parc_JSON.h>
+#include <ccnx/common/ccnx_Name.h>
+
+struct notify_status;
+/**
+ * @typedef NotifyStatus
+ * @brief Notifications from Transport
+ */
+typedef struct notify_status NotifyStatus;
+
+// This needs to be replaced with a more sophisticated encoding scheme that individual stack components can use. Case 1035
+/**
+ * @typedef NotifyStatusCode
+ * @brief Codes for Notify Status
+ */
+typedef enum {
+ // TRANSPORT_READY = 1, // returned when Transport_Create finished
+ // TRANSPORT_DESTROYED = 2, // when Transport_Destroy is done
+ notifyStatusCode_OPEN_ERROR = 3, // error when opening a connection stack
+ notifyStatusCode_CONNECTION_OPEN = 4, // returned when a connection is opened
+ notifyStatusCode_CONNECTION_CLOSED = 5, // returned when close is finished
+ notifyStatusCode_FORWARDER_NOT_AVAILABLE = 6, // connection problem with forwarder
+ notifyStatusCode_FLOW_CONTROL_STARTED = 7, // when flow control starts on a name
+ notifyStatusCode_FLOW_CONTROL_FINISHED = 8, // after final block is passed up
+ notifyStatusCode_FLOW_CONTROL_ERROR = 9, // some hard error on the name
+ notifyStatusCode_ENCODING_ERROR = 10, // something bad in the codec
+ notifyStatusCode_SIGNING_ERROR = 11, // error signing
+ notifyStatusCode_SEND_ERROR = 12, // some other "down" stack error
+} NotifyStatusCode;
+
+/**
+ * @typedef NotifyStatusDirection
+ * @brief The direction of the NotifyStatus
+ */
+typedef enum {
+ notifyStatusDirection_UPSTACK,
+ notifyStatusDirection_DOWNSTACK
+} NotifyStatusDirection;
+
+/**
+ * Create an instance of `NotifyStatus`.
+ *
+ * @param [in] apiFd The corresponding api file descriptor.
+ * @param [in] code The NotifyStatusCode for this status.
+ * @param [in] name An associated CCNxName
+ * @param [in] message An (optional) string message
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid NotifyStatus instance that must be released via notifyStatus_Release().
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *expected = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ * }
+ * @endcode
+ *
+ * @see {@link notifyStatus_Release}
+ */
+NotifyStatus *notifyStatus_Create(int apiFd, NotifyStatusCode code, CCNxName *name, const char *message);
+
+/**
+ * Increase the number of references to a `NotifyStatus`.
+ *
+ * Note that new `NotifyStatus` is not created,
+ * only that the given `NotifyStatus` reference count is incremented.
+ * Discard the reference by invoking {@link notifyStatus_Release}.
+ *
+ * @param [in] status A pointer to a `NotifyStatus` instance.
+ * @return The input `NotifyStatus` pointer.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * NotifyStatus *x_2 = notifyStatus_Acquire(status);
+ *
+ * notifyStatus_Release(&status);
+ * notifyStatus_Release(&x_2);
+ * }
+ * @endcode
+ */
+NotifyStatus *notifyStatus_Acquire(const NotifyStatus *status);
+
+/**
+ * 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] statusPtr A pointer to a pointer to the instance to release.
+ *
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * notifyStatus_Release(&status);
+ * }
+ * @endcode
+ */
+void notifyStatus_Release(NotifyStatus **statusPtr);
+
+/**
+ * Returns true if contents of two NotifyStatus objects are the same.
+ *
+ * @param [in] x object 1
+ * @param [in] y object 2
+ *
+ * @return true X & Y are equal
+ * @return false X & Y are not equal
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status1 = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ * NotifyStatus *status2 = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * if (notifyStatus_Equals(status1, status2)) {
+ * ...
+ * }
+ * }
+ * @endcode
+ */
+bool notifyStatus_Equals(const NotifyStatus *x, const NotifyStatus *y);
+
+/**
+ * Print a human readable representation of the given `NotifyStatus`.
+ *
+ * @param [in] status A pointer to the instance to display.
+ * @param [in] indentation The level of indentation to use to pretty-print the output.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * notifyStatus_Display(status, 0);
+ *
+ * notifyStatus_Release(&status);
+ * }
+ * @endcode
+ */
+void notifyStatus_Display(const NotifyStatus *status, int indentation);
+
+/**
+ * Get the associated file descriptor of the given `NotifyStatus` instance.
+ *
+ * @param [in] status A pointer to a valid instance of `NotifyStatus`.
+ *
+ * @return The associated file descriptor of this NotifyStatus instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * int fd = notifyStatus_GetFiledes(status);
+ * }
+ * @endcode
+ */
+int notifyStatus_GetFiledes(const NotifyStatus *status);
+
+/**
+ * Get the associated {@link NotifyStatusCode} of the given `NotifyStatus` instance.
+ *
+ * @param [in] status A pointer to a valid instance of `NotifyStatus`.
+ *
+ * @return The associated NotifyStatusCode of the given `NotifyStatus` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * NotifyStatusCode code = notifyStatus_GetStatusCode(status);
+ * }
+ * @endcode
+ */
+NotifyStatusCode notifyStatus_GetStatusCode(const NotifyStatus *status);
+
+/**
+ * Get the associated {@link CCNxName} of the given `NotifyStatus` instance.
+ *
+ * @param [in] status A pointer to a valid instance of `NotifyStatus`.
+ *
+ * @return The `CCNxName` of this `NotifyStatus` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * NotifyStatusCode code = notifyStatus_GetStatusCode(status);
+ * }
+ * @endcode
+ */
+CCNxName *notifyStatus_GetName(const NotifyStatus *status);
+
+/**
+ * Get the associated {@link CCNxName} of the given `NotifyStatus` instance.
+ *
+ * @param [in] status A pointer to a valid instance of `NotifyStatus`.
+ *
+ * @return The message of this NotifyStatus instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * NotifyStatusCode code = notifyStatus_GetStatusCode(status);
+ * }
+ * @endcode
+ */
+char *notifyStatus_GetMessage(const NotifyStatus *status);
+
+/**
+ * Return a {@link PARCJSON} representation of the given `NotifyStatus` instance.
+ *
+ * @param [in] status A pointer to a valid instance of `NotifyStatus`.
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid `PARCJSON` instance
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * PARCJSON *json = notifyStatus_ToJSON(status);
+ * }
+ * @endcode
+ */
+PARCJSON *notifyStatus_ToJSON(const NotifyStatus *status);
+
+/**
+ * Create a new `NotifyStatus` instance from a {@link PARCJSON} instance.
+ *
+ * @param [in] json A pointer to a `PARCJSON` instance.
+ *
+ * @return NULL An error occurred
+ * @return non-NULL A pointer to a valid `NotifyStatus` instance.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * PARCJSON *json = notifyStatus_ToJSON(status);
+ *
+ * NotifyStatus *status2 = notifyStatus_ParseJSON(json)
+ * }
+ * @endcode
+ */
+NotifyStatus *notifyStatus_ParseJSON(const PARCJSON *json);
+
+/**
+ * Evaluate to `true` if the given `NotifyStatus` indicates a Connection Open.
+ *
+ * @param [in] status A pointer to a `NotifyStatus` instance.
+ *
+ * @return `true` The given `NotifyStatus` indicates a Connection open.
+ * @return `false` The given ``NotifyStatus` indicates that the Connection is not open.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * if (notifyStatus_IsConnectionOpen(status)) {
+ * printf("Connection is open\n");
+ * }
+ * }
+ * @endcode
+ */
+bool notifyStatus_IsConnectionOpen(const NotifyStatus *status);
+
+/**
+ * Return `true` if the given status indicates that the flow control has started.
+ *
+ * @param [in] status A pointer to a NotifyStatus instance.
+ *
+ * @return `true` The given `NotifyStatu`s indicates that flow controller has started
+ * @return `false` The given `NotifyStatus` indicates that the flow controller has not started.
+ *
+ * Example:
+ * @code
+ * {
+ * CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ * NotifyStatus *status = notifyStatus_Create(1, notifyStatus_CONNECTION_OPEN, name, "Good to go");
+ *
+ * if (notifyStatus_IsFlowControlStarted(status)) {
+ * printf("Flow controller has started\n");
+ * }
+ * }
+ * @endcode
+ */
+bool notifyStatus_IsFlowControlStarted(const NotifyStatus *status);
+#endif // Libccnx_notifyStatus_h
diff --git a/libccnx-transport-rta/ccnx/api/notify/notify_Timer.h b/libccnx-transport-rta/ccnx/api/notify/notify_Timer.h
new file mode 100644
index 00000000..179d57ba
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/notify_Timer.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_notify_Timer_h
+#define Libccnx_notify_Timer_h
+#endif // Libccnx_notify_Timer_h
diff --git a/libccnx-transport-rta/ccnx/api/notify/test/.gitignore b/libccnx-transport-rta/ccnx/api/notify/test/.gitignore
new file mode 100644
index 00000000..64e67423
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/test/.gitignore
@@ -0,0 +1 @@
+test_notify_Status
diff --git a/libccnx-transport-rta/ccnx/api/notify/test/CMakeLists.txt b/libccnx-transport-rta/ccnx/api/notify/test/CMakeLists.txt
new file mode 100644
index 00000000..97b14602
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/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_notify_Status
+)
+
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/libccnx-transport-rta/ccnx/api/notify/test/test_notify_Status.c b/libccnx-transport-rta/ccnx/api/notify/test/test_notify_Status.c
new file mode 100644
index 00000000..04269b0a
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/api/notify/test/test_notify_Status.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.
+ */
+
+
+// Include the file(s) containing the functions to be tested.
+// This permits internal static functions to be visible to this Test Framework.
+#include "../notify_Status.c"
+
+#include <LongBow/unit-test.h>
+
+LONGBOW_TEST_RUNNER(notify_Status)
+{
+ // 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(notify_Status)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(notify_Status)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, notifyStatus_ToJSON);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, notifyStatus_ToJSON)
+{
+ CCNxName *name = ccnxName_CreateFromCString("lci:/a/b/c");
+ NotifyStatus *expected = notifyStatus_Create(1, notifyStatusCode_CONNECTION_OPEN, name, "foo");
+
+ PARCJSON *json = notifyStatus_ToJSON(expected);
+
+ NotifyStatus *actual = notifyStatus_ParseJSON(json);
+
+ assertTrue(notifyStatus_Equals(expected, actual), "Failed to create and parse NotifyStatus")
+ {
+ notifyStatus_Display(expected, 0);
+ notifyStatus_Display(actual, 0);
+ }
+}
+
+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(notify_Status);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/libccnx-transport-rta/ccnx/config.h.in b/libccnx-transport-rta/ccnx/config.h.in
new file mode 100644
index 00000000..5d047cfc
--- /dev/null
+++ b/libccnx-transport-rta/ccnx/config.h.in
@@ -0,0 +1,4 @@
+/* CPU Cache line size */
+#define LEVEL1_DCACHE_LINESIZE @LEVEL1_DCACHE_LINESIZE@
+
+#define _GNU_SOURCE
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);
+}