aboutsummaryrefslogtreecommitdiffstats
path: root/libccnx-transport-rta/ccnx/api
diff options
context:
space:
mode:
Diffstat (limited to 'libccnx-transport-rta/ccnx/api')
-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
99 files changed, 20497 insertions, 0 deletions
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);
+}