aboutsummaryrefslogtreecommitdiffstats
path: root/metis/ccnx/forwarder/metis/test
diff options
context:
space:
mode:
authormichele papalini <micpapal+fdio@cisco.com>2017-02-23 17:01:34 +0100
committerMichele Papalini <micpapal+fdio@cisco.com>2017-02-23 17:23:19 +0000
commitc580a00aac271a524e5a75b35f4b91c174ed227b (patch)
treefeddc15a9f1a4eb319d950f8d6330ac2b91e3d99 /metis/ccnx/forwarder/metis/test
parent9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (diff)
Initial commit: sb-forwarder, metis.
Change-Id: I65ee3c851a6901929ef4417ad80d34bca0dce445 Signed-off-by: michele papalini <micpapal+fdio@cisco.com>
Diffstat (limited to 'metis/ccnx/forwarder/metis/test')
-rw-r--r--metis/ccnx/forwarder/metis/test/CMakeLists.txt16
-rw-r--r--metis/ccnx/forwarder/metis/test/test_sys_Errors.c293
-rw-r--r--metis/ccnx/forwarder/metis/test/test_sys_EtherEndToEnd.c347
-rw-r--r--metis/ccnx/forwarder/metis/test/test_sys_TcpEndToEnd.c249
-rw-r--r--metis/ccnx/forwarder/metis/test/test_sys_TcpTunnel.c298
-rw-r--r--metis/ccnx/forwarder/metis/test/test_sys_UdpEndToEnd.c255
6 files changed, 1458 insertions, 0 deletions
diff --git a/metis/ccnx/forwarder/metis/test/CMakeLists.txt b/metis/ccnx/forwarder/metis/test/CMakeLists.txt
new file mode 100644
index 00000000..9cdcac48
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Enable gcov output for the tests
+add_definitions(--coverage)
+set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} " --coverage")
+
+set(TestsExpectedToPass
+ test_sys_Errors
+ test_sys_EtherEndToEnd
+ test_sys_TcpEndToEnd
+ test_sys_UdpEndToEnd
+ test_sys_TcpTunnel
+)
+
+foreach(test ${TestsExpectedToPass})
+ AddTest(${test})
+endforeach()
+
diff --git a/metis/ccnx/forwarder/metis/test/test_sys_Errors.c b/metis/ccnx/forwarder/metis/test/test_sys_Errors.c
new file mode 100644
index 00000000..956348b1
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/test_sys_Errors.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * These are tests for error conditions, mostly in packet formats.
+ */
+
+#include <config.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <net/ethernet.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+#include <ccnx/forwarder/metis/io/metis_UdpListener.h>
+#include <ccnx/forwarder/metis/io/metis_TcpListener.h>
+#include <ccnx/forwarder/metis/io/metis_EtherListener.h>
+#include <ccnx/forwarder/metis/io/metis_EtherConnection.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV1.h>
+
+#include <ccnx/forwarder/metis/io/test/testrig_GenericEther.c>
+
+LONGBOW_TEST_RUNNER(sys_Errors)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+// The Test Runner calls this function once before any Test Fixtures are run.
+LONGBOW_TEST_RUNNER_SETUP(sys_Errors)
+{
+ 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(sys_Errors)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ---- Used to monitor Missive messages so we know when
+// a connection is setup
+static struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} testNotifierData;
+
+static void
+_testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ---- Utility functions to setup endpoints
+
+static void
+_setupListener(MetisForwarder *metis, uint16_t port, MetisEncapType type)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ MetisListenerSet *listeners = metisForwarder_GetListenerSet(metis);
+ MetisListenerOps *ops = NULL;
+
+ switch (type) {
+ case METIS_ENCAP_UDP:
+ ops = metisUdpListener_CreateInet(metis, addr);
+ break;
+
+ case METIS_ENCAP_TCP:
+ ops = metisTcpListener_CreateInet(metis, addr);
+ break;
+
+ case METIS_ENCAP_ETHER:
+ ops = metisEtherListener_Create(metis, "fake0", port);
+ break;
+
+ default:
+ trapUnexpectedState("Unsupported ");
+ }
+
+ assertNotNull(ops, "Got null io operations");
+
+ metisListenerSet_Add(listeners, ops);
+
+ // crank the handle once
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+}
+
+static int
+_setupInetClient(MetisForwarder *metis, uint16_t port, int type)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ int fd = socket(PF_INET, type, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ int failure = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ // crank the handle once
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+ return fd;
+}
+
+// ---- Global state used by tests
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ MetisMessengerRecipient *recipient;
+
+ int fd_sender;
+} TestData;
+
+static void
+_commonSetup(const LongBowTestCase *testCase)
+{
+ TestData *data = parcMemory_Allocate(sizeof(TestData));
+
+ data->metis = metisForwarder_Create(NULL);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_IO, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_Message, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_Core, PARCLogLevel_Debug);
+ metisLogger_SetLogLevel(metisForwarder_GetLogger(data->metis), MetisLoggerFacility_Processor, PARCLogLevel_Debug);
+
+ data->recipient = metisMessengerRecipient_Create(&testNotifierData, _testNotifier);
+
+ // register a messenger callback so we know when the connections get setup
+ MetisMessenger *messenger = metisForwarder_GetMessenger(data->metis);
+ metisMessenger_Register(messenger, data->recipient);
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+_setupInetEncap(const LongBowTestCase *testCase, uint16_t port, MetisEncapType encap)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ _setupListener(data->metis, port, encap);
+
+ // create a client
+ switch (encap) {
+ case METIS_ENCAP_UDP:
+ data->fd_sender = _setupInetClient(data->metis, port, SOCK_DGRAM);
+ break;
+
+ case METIS_ENCAP_TCP:
+ data->fd_sender = _setupInetClient(data->metis, port, SOCK_STREAM);
+ break;
+
+ default:
+ trapUnexpectedState("Unsupported encap type: %d", encap);
+ }
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 1000 }));
+
+
+ // send something to actually connect it, this is a good packet
+ ssize_t nwritten = write(data->fd_sender, metisTestDataV1_Interest_NameA_Crc32c, sizeof(metisTestDataV1_Interest_NameA_Crc32c));
+ assertTrue(nwritten == sizeof(metisTestDataV1_Interest_NameA_Crc32c), "Short write, expected %zu got %zd", sizeof(metisTestDataV1_Interest_NameA_Crc32c), nwritten);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 1000 }));
+
+ printf("sender port %u connection id = %u\n", port, testNotifierData.connectionid);
+}
+
+static void
+_commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ metisForwarder_Destroy(&data->metis);
+ metisMessengerRecipient_Destroy(&data->recipient);
+
+ close(data->fd_sender);
+ parcMemory_Deallocate((void **) &data);
+}
+
+// ==========================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, udp);
+ LONGBOW_RUN_TEST_CASE(Global, ether);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ _commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ _commonTeardown(testCase);
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, udp)
+{
+ _setupInetEncap(testCase, 44999, METIS_ENCAP_UDP);
+
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ for (int i = 0; metisTestDataV1_ErrorFrames[i].frame != NULL; i++) {
+ const uint8_t *frame = metisTestDataV1_ErrorFrames[i].frame;
+ const size_t length = metisTestDataV1_ErrorFrames[i].length;
+
+ printf("Writing frame %d length %zu\n", i, length);
+
+ const ssize_t nwritten = write(data->fd_sender, frame, length);
+ assertTrue(nwritten == length, "Short write, expected %zu got %zd", length, nwritten);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 1000 }));
+ }
+}
+
+LONGBOW_TEST_CASE(Global, ether)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ _setupListener(data->metis, 0x0801, METIS_ENCAP_ETHER);
+
+ // there's only 1 listener, so it's at index 0
+ MetisListenerOps *listener = metisListenerSet_Get(metisForwarder_GetListenerSet(data->metis), 0);
+ MetisGenericEther *ether = metisEtherListener_GetGenericEtherFromListener(listener);
+ PARCBuffer *localAddressBuffer = metisGenericEther_GetMacAddress(ether);
+ uint8_t *dmac = parcBuffer_Overlay(localAddressBuffer, 0);
+ uint8_t smac[ETHER_ADDR_LEN] = { 1, 2, 3, 4, 5, 6 };
+
+ // we're now ready to start receiving data. We "send" data to the mock ethernet
+ // by using the API to the mock GenericEther.
+
+ for (int i = 0; metisTestDataV1_ErrorFrames[i].frame != NULL; i++) {
+ const uint8_t *frame = metisTestDataV1_ErrorFrames[i].frame;
+ const size_t length = metisTestDataV1_ErrorFrames[i].length;
+
+ printf("Writing frame %d length %zu\n", i, length);
+
+ PARCBuffer *buffer = mockGenericEther_createFrame(length, frame, dmac, smac, 0x0801);
+ mockGenericEther_QueueFrame(ether, buffer);
+ mockGenericEther_Notify(ether);
+ parcBuffer_Release(&buffer);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 1000 }));
+ }
+}
+
+
+// =================================================
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(sys_Errors);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/test/test_sys_EtherEndToEnd.c b/metis/ccnx/forwarder/metis/test/test_sys_EtherEndToEnd.c
new file mode 100644
index 00000000..7061f74c
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/test_sys_EtherEndToEnd.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * These are end-to-end system tests. They nail up two Ethernet connections, setup a FIB entry, and send
+ * and interest then a content object back.
+ *
+ * To Metis, it looks like it has 2 ethernet interfaces, "fake0" and "fake1". We send an Interest
+ * in "fake0" and it should come out "fake1", then send a content object back the other way.
+ *
+ * Uses the mock GenericEthernet object so there's no actual network required.
+ *
+ */
+
+#include <config.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <errno.h>
+
+// include the mock Ethernet implementation
+#include "../io/test/testrig_GenericEther.c"
+#include <ccnx/forwarder/metis/io/metis_EtherListener.c>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+#define ETHERTYPE 0x0801
+
+LONGBOW_TEST_RUNNER(sys_EtherEndToEnd)
+{
+ LONGBOW_RUN_TEST_FIXTURE(Global);
+}
+
+LONGBOW_TEST_RUNNER_SETUP(sys_EtherEndToEnd)
+{
+ parcMemory_SetInterface(&PARCSafeMemoryAsPARCMemory);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_RUNNER_TEARDOWN(sys_EtherEndToEnd)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/*
+ * Used to monitor Missive messages so we know when
+ * a connection is setup
+ */
+static struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} testNotifierData;
+
+static void
+_testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ---- Utility functions to setup Ethernet endpoints
+
+static MetisListenerOps *
+_setupEtherListener(MetisForwarder *metis, const char *devName, uint16_t ethertype)
+{
+ MetisListenerSet *listeners = metisForwarder_GetListenerSet(metis);
+ MetisListenerOps *ops = metisEtherListener_Create(metis, devName, ethertype);
+ metisListenerSet_Add(listeners, ops);
+
+ // crank the libevent handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+ return ops;
+}
+
+// ---- Clipboard state used by tests
+
+typedef struct test_data {
+ MetisForwarder *metis;
+ MetisMessengerRecipient *recipient;
+
+#define CLIENTSIDE 0
+#define SERVERSIDE 1
+
+ MetisListenerOps *etherListener[2];
+ MetisGenericEther *ether[2];
+
+ // These are the peer addresses off ether0 and ether1 (i.e. the "remote" systems
+ // that are sending the frames to metis).
+ uint8_t ether_peerAddress[ETHER_ADDR_LEN][2];
+} TestData;
+
+static void
+_sendPing(TestData *data, int side)
+{
+ PARCBuffer *ether0_mac = metisGenericEther_GetMacAddress(data->ether[side]);
+ PARCBuffer *frame = mockGenericEther_createFrame(sizeof(metisTestDataV0_EncodedInterest), metisTestDataV0_EncodedInterest, parcBuffer_Overlay(ether0_mac, 0), data->ether_peerAddress[side], ETHERTYPE);
+
+ mockGenericEther_QueueFrame(data->ether[side], frame);
+ mockGenericEther_Notify(data->ether[side]);
+
+ parcBuffer_Release(&frame);
+}
+
+static void
+_bringUpListener(TestData *data, int side, const char *devName)
+{
+ data->etherListener[side] = _setupEtherListener(data->metis, devName, ETHERTYPE);
+ data->ether[side] = ((_MetisEtherListener *) data->etherListener[side]->context)->genericEther;
+
+ // send a ping to Metis to bring up a connection
+ _sendPing(data, side);
+
+ // crank the dispatcher handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) {0, 10000}));
+ printf("side %d connection id = %u\n", side, testNotifierData.connectionid);
+}
+
+static void
+_commonSetup(const LongBowTestCase *testCase)
+{
+ TestData *data = parcMemory_Allocate(sizeof(TestData));
+
+ data->metis = metisForwarder_Create(NULL);
+
+ // setup a messenger recpient so we get a notification when the connection is up
+ data->recipient = metisMessengerRecipient_Create(&testNotifierData, _testNotifier);
+
+ // setup the peer addresses (need to do this before bringing up the listeners)
+ memset(data->ether_peerAddress[CLIENTSIDE], 0xAA, ETHER_ADDR_LEN);
+ memset(data->ether_peerAddress[SERVERSIDE], 0xBB, ETHER_ADDR_LEN);
+
+ // register a messenger callback so we know when the connections get setup
+ MetisMessenger *messenger = metisForwarder_GetMessenger(data->metis);
+ metisMessenger_Register(messenger, data->recipient);
+
+ _bringUpListener(data, CLIENTSIDE, "fake0");
+
+ _bringUpListener(data, SERVERSIDE, "fake1");
+
+ // Add a FIB entry out the receiver connection (testNotifierData.connectionid will be set to the "fake1" connection id
+ // because it was the last thing to get a missive sent)
+ CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch");
+ CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, testNotifierData.connectionid, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ metisForwarder_AddOrUpdateRoute(data->metis, routeAdd);
+ cpiRouteEntry_Destroy(&routeAdd);
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 10000 }));
+
+ longBowTestCase_SetClipBoardData(testCase, data);
+}
+
+static void
+_commonTeardown(const LongBowTestCase *testCase)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ metisForwarder_Destroy(&data->metis);
+ metisMessengerRecipient_Destroy(&data->recipient);
+ parcMemory_Deallocate((void **) &data);
+}
+
+// ==========================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, passInterest_Unicast);
+ LONGBOW_RUN_TEST_CASE(Global, returnContentObject);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ _commonSetup(testCase);
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ _commonTeardown(testCase);
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+/*
+ * Send the Interest to the client socket (ether0).
+ * Returns the frame sent (with Ethernet header)
+ */
+static PARCBuffer *
+_sendInterest(TestData *data)
+{
+ PARCBuffer *ether0_mac = metisGenericEther_GetMacAddress(data->ether[CLIENTSIDE]);
+ PARCBuffer *frame = mockGenericEther_createFrame(sizeof(metisTestDataV0_InterestWithName_keyid), metisTestDataV0_InterestWithName_keyid, parcBuffer_Overlay(ether0_mac, 0), data->ether_peerAddress[CLIENTSIDE], ETHERTYPE);
+
+ mockGenericEther_QueueFrame(data->ether[CLIENTSIDE], frame);
+
+ // wake up the client's side of metis to receive a frame
+ mockGenericEther_Notify(data->ether[CLIENTSIDE]);
+
+ return frame;
+}
+
+/*
+ * Receives an Interest Ethernet frame on the server's side of Metis and verifies it matches what was sent
+ * Has to recompute the hop-limit in the truthFrame, uses magic knowledge of V0 packets.
+ */
+static void
+_receiveInterest(TestData *data, PARCBuffer *truthFrame)
+{
+ uint8_t receiveBuffer[1024];
+
+ int serverSocket = mockGenericEther_GetTestDescriptor(data->ether[SERVERSIDE]);
+
+ ssize_t read_length = read(serverSocket, receiveBuffer, 1024);
+ assertTrue(read_length == parcBuffer_Remaining(truthFrame),
+ "Incorrect read, expected %zd got %zd: (%d) %s",
+ parcBuffer_Remaining(truthFrame), read_length, errno, strerror(errno));
+
+ // skip over the ethernet header
+ parcBuffer_SetPosition(truthFrame, sizeof(struct ether_header));
+
+ // We decrement the hoplimit, so need a new truth value
+ // TODO: There's a lot of magic knowledge that the hoplimit is at byte offset 12 and
+ // originally had the value 32.
+ parcBuffer_PutAtIndex(truthFrame, sizeof(struct ether_header) + 12, 31);
+
+ PARCBuffer *testFrame = parcBuffer_Wrap(receiveBuffer, parcBuffer_Limit(truthFrame), sizeof(struct ether_header), parcBuffer_Limit(truthFrame));
+ assertTrue(parcBuffer_Equals(truthFrame, testFrame), "Messages do not match")
+ {
+ parcBuffer_Display(truthFrame, 3);
+ parcBuffer_Display(testFrame, 3);
+ }
+
+ parcBuffer_Release(&testFrame);
+}
+
+/*
+ * Send the Interest to the client socket (ether0).
+ * Returns the frame sent (with Ethernet header)
+ */
+static PARCBuffer *
+_sendContentObject(TestData *data)
+{
+ // metisTestDataV0_EncodedObject has the same name and keyid as in the Interest
+
+ PARCBuffer *ether0_mac = metisGenericEther_GetMacAddress(data->ether[SERVERSIDE]);
+ PARCBuffer *frame = mockGenericEther_createFrame(sizeof(metisTestDataV0_EncodedObject), metisTestDataV0_EncodedObject, parcBuffer_Overlay(ether0_mac, 0), data->ether_peerAddress[SERVERSIDE], ETHERTYPE);
+
+ mockGenericEther_QueueFrame(data->ether[SERVERSIDE], frame);
+
+ // wake up the server's side of metis to receive a frame
+ mockGenericEther_Notify(data->ether[SERVERSIDE]);
+
+ return frame;
+}
+
+/*
+ * Receives a ContentObject Ethernet frame on the client's side of Metis and verifies it matches what was sent
+ */
+static void
+_receiveContentObject(TestData *data, PARCBuffer *truthFrame)
+{
+ uint8_t receiveBuffer[1024];
+
+ int serverSocket = mockGenericEther_GetTestDescriptor(data->ether[CLIENTSIDE]);
+
+ ssize_t read_length = read(serverSocket, receiveBuffer, 1024);
+ assertTrue(read_length == parcBuffer_Remaining(truthFrame),
+ "Incorrect read, expected %zd got %zd: (%d) %s",
+ parcBuffer_Remaining(truthFrame), read_length, errno, strerror(errno));
+
+ // skip over the ethernet header
+ parcBuffer_SetPosition(truthFrame, sizeof(struct ether_header));
+
+ PARCBuffer *testFrame = parcBuffer_Wrap(receiveBuffer, parcBuffer_Limit(truthFrame), sizeof(struct ether_header), parcBuffer_Limit(truthFrame));
+ assertTrue(parcBuffer_Equals(truthFrame, testFrame), "Messages do not match")
+ {
+ parcBuffer_Display(truthFrame, 3);
+ parcBuffer_Display(testFrame, 3);
+ }
+
+ parcBuffer_Release(&testFrame);
+}
+
+LONGBOW_TEST_CASE(Global, passInterest_Unicast)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+
+ PARCBuffer *truthFrame = _sendInterest(data);
+
+ // run for a duration so libevent has time to read the message, pass it off to the handler, then send the message out
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 10000 }));
+
+ _receiveInterest(data, truthFrame);
+
+ parcBuffer_Release(&truthFrame);
+}
+
+LONGBOW_TEST_CASE(Global, returnContentObject)
+{
+ TestData *data = longBowTestCase_GetClipBoardData(testCase);
+
+ // send the Interest so we have a PIT entry
+
+ PARCBuffer *truthInterest = _sendInterest(data);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 10000 }));
+ _receiveInterest(data, truthInterest);
+ parcBuffer_Release(&truthInterest);
+
+ // send the content object back
+
+ PARCBuffer *truthContentObject = _sendContentObject(data);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(data->metis), &((struct timeval) { 0, 10000 }));
+ _receiveContentObject(data, truthContentObject);
+ parcBuffer_Release(&truthContentObject);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(sys_EtherEndToEnd);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
+
diff --git a/metis/ccnx/forwarder/metis/test/test_sys_TcpEndToEnd.c b/metis/ccnx/forwarder/metis/test/test_sys_TcpEndToEnd.c
new file mode 100644
index 00000000..2d045110
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/test_sys_TcpEndToEnd.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * These are end-to-end system tests. They nail up two TCP connections, setup a FIB entry, and send
+ * and interest then a content object back.
+ */
+
+#include <config.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+#include <ccnx/forwarder/metis/io/metis_TcpListener.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+LONGBOW_TEST_RUNNER(sys_TcpEndToEnd)
+{
+ // 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(sys_TcpEndToEnd)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(sys_TcpEndToEnd)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ---- Used to monitor Missive messages so we know when
+// a connection is setup
+static struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} testNotifierData;
+
+static void
+testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ---- Utility functions to setup TCP endpoints
+
+static void
+setupInetListener(MetisForwarder *metis, uint16_t port)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ MetisListenerSet *listeners = metisForwarder_GetListenerSet(metis);
+ MetisListenerOps *ops = metisTcpListener_CreateInet(metis, addr);
+ metisListenerSet_Add(listeners, ops);
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+}
+
+static int
+setupInetClient(MetisForwarder *metis, uint16_t port)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ int fd = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ int failure = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+ return fd;
+}
+
+// ---- Global state used by tests
+
+static struct global_state {
+ MetisForwarder *metis;
+ MetisMessengerRecipient *recipient;
+ int fd_sender;
+ int fd_receiver;
+} globalState;
+
+static void
+setupTcp(void)
+{
+ globalState.metis = metisForwarder_Create(NULL);
+ globalState.recipient = metisMessengerRecipient_Create(&testNotifierData, testNotifier);
+
+ // register a messenger callback so we know when the connections get setup
+ MetisMessenger *messenger = metisForwarder_GetMessenger(globalState.metis);
+ metisMessenger_Register(messenger, globalState.recipient);
+
+ setupInetListener(globalState.metis, 49996);
+ setupInetListener(globalState.metis, 49997);
+
+ // create two test connections and learn their connection IDs via
+ // the messenger callback
+
+ globalState.fd_sender = setupInetClient(globalState.metis, 49996);
+ metisDispatcher_RunCount(metisForwarder_GetDispatcher(globalState.metis), 1);
+ printf("sender connection id = %u\n", testNotifierData.connectionid);
+
+ globalState.fd_receiver = setupInetClient(globalState.metis, 49997);
+ metisDispatcher_RunCount(metisForwarder_GetDispatcher(globalState.metis), 1);
+ unsigned receiverConnectionId = testNotifierData.connectionid;
+ printf("receiver connection id = %u\n", testNotifierData.connectionid);
+
+ // Add a FIB entry out the receiver connection
+ CCNxName *ccnxNameToAdd =
+ ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch");
+
+ CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, receiverConnectionId, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ metisForwarder_AddOrUpdateRoute(globalState.metis, routeAdd);
+ cpiRouteEntry_Destroy(&routeAdd);
+
+ metisDispatcher_RunCount(metisForwarder_GetDispatcher(globalState.metis), 1);
+}
+
+static void
+teardownTcp(void)
+{
+ metisForwarder_Destroy(&globalState.metis);
+ metisMessengerRecipient_Destroy(&globalState.recipient);
+
+ close(globalState.fd_sender);
+ close(globalState.fd_receiver);
+}
+
+// ==========================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, passInterest);
+ //LONGBOW_RUN_TEST_CASE(Global, returnContentObject);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ setupTcp();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ teardownTcp();
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, passInterest)
+{
+ uint8_t receiveBuffer[1024];
+
+ // now send the interest on the sender and see if we get it on the receiver
+ ssize_t write_length = write(globalState.fd_sender, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName),
+ "Partial write, expected %zd got %zd: (%d) %s",
+ sizeof(metisTestDataV0_InterestWithName), write_length, errno, strerror(errno));
+ // run for a duration so there is time to read the message, pass it off to the handler, then send the message out
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 10000 }));
+
+ ssize_t read_length = read(globalState.fd_receiver, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_InterestWithName), "Incorrect read, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), read_length, errno, strerror(errno));
+
+ // We decrement the hoplimit, so need a new truth value
+ uint8_t truth[1024];
+ memcpy(truth, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ //truth[12] = 31;
+
+ assertTrue(memcmp(receiveBuffer, truth, read_length) == 0, "Messages do not match");
+}
+
+LONGBOW_TEST_CASE(Global, returnContentObject)
+{
+ uint8_t receiveBuffer[1024];
+
+ // send the interest on the sender and see if we get it on the receiver
+ ssize_t write_length = write(globalState.fd_sender, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName), "Partial write of interest, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), write_length, errno, strerror(errno));
+
+ // run for a duration so there is time to read the message, pass it off to the handler, then send the message out
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 1000 }));
+
+ ssize_t read_length = read(globalState.fd_receiver, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_InterestWithName), "Incorrect read of interest, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), read_length, errno, strerror(errno));
+
+ // send content object back
+ write_length = write(globalState.fd_receiver, metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject));
+ assertTrue(write_length == sizeof(metisTestDataV0_EncodedObject), "Partial write of object, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_EncodedObject), write_length, errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 1000 }));
+
+ // now check that we got the object
+ read_length = read(globalState.fd_sender, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_EncodedObject), "Incorrect read of object, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_EncodedObject), read_length, errno, strerror(errno));
+
+ assertTrue(memcmp(receiveBuffer, metisTestDataV0_EncodedObject, read_length) == 0, "Objects do not match");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(sys_TcpEndToEnd);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/test/test_sys_TcpTunnel.c b/metis/ccnx/forwarder/metis/test/test_sys_TcpTunnel.c
new file mode 100644
index 00000000..0ce2525d
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/test_sys_TcpTunnel.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Run two instances of metis.
+ * Client_1 - Metis_A - Metis_B - Client_2
+ *
+ * Steps
+ * 1) run two instances of Metis
+ * 2) Create TCP listeners on 127.0.0.1:10001 and 127.0.0.1:10002
+ * 3) create a tunnel from A->B.
+ * 4) setup route to /foo from a to b
+ * 5) Connect client 1 to A
+ * 6) Connect client 2 to B
+ * 7) Setup route to /foo from metis B to client 2.
+ * 8) Sent interest from #1 to #2
+ * 9) Send object back from #2 to #1
+ *
+ */
+
+#include <config.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+#include <ccnx/forwarder/metis/io/metis_TcpTunnel.h>
+#include <ccnx/forwarder/metis/config/metis_Configuration.h>
+#include <ccnx/forwarder/metis/config/metis_ConfigurationListeners.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+LONGBOW_TEST_RUNNER(test_sys_TcpTunnel)
+{
+ // 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(test_sys_TcpTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(test_sys_TcpTunnel)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ LONGBOW_RUN_TEST_CASE(Global, tcpTunnel);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+typedef struct notify_receiver {
+ MetisMissive *lastMessage;
+} NotifyReceiver;
+
+static void
+missiveNotify(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ NotifyReceiver *receiver = (NotifyReceiver *) metisMessengerRecipient_GetRecipientContext(recipient);
+ if (receiver->lastMessage != NULL) {
+ metisMissive_Release(&receiver->lastMessage);
+ }
+ receiver->lastMessage = missive;
+}
+
+LONGBOW_TEST_CASE(Global, tcpTunnel)
+{
+ uint16_t metisA_port = 10001;
+ uint16_t metisB_port = 10002;
+
+ // these will get filled in with the most recent message
+ NotifyReceiver receiver_a = { NULL };
+ NotifyReceiver receiver_b = { NULL };
+
+ MetisMessengerRecipient *recipient_a = metisMessengerRecipient_Create(&receiver_a, missiveNotify);
+ MetisMessengerRecipient *recipient_b = metisMessengerRecipient_Create(&receiver_b, missiveNotify);
+
+ // in between each step, run the dispatchers for 1 msec to let things settle.
+
+ // ===============================================
+ /* 1) run two instances of Metis */
+ MetisForwarder *metis_a = metisForwarder_Create(NULL);
+ MetisForwarder *metis_b = metisForwarder_Create(NULL);
+
+ MetisDispatcher *dispatcher_a = metisForwarder_GetDispatcher(metis_a);
+ MetisDispatcher *dispatcher_b = metisForwarder_GetDispatcher(metis_b);
+
+ // register to receive notifications
+ metisMessenger_Register(metisForwarder_GetMessenger(metis_a), recipient_a);
+ metisMessenger_Register(metisForwarder_GetMessenger(metis_b), recipient_b);
+
+ // ===============================================
+ /* 2) Create TCP listeners on 127.0.0.1:10001 and 10002 */
+
+ metisConfigurationListeners_SetupAll(metisForwarder_GetConfiguration(metis_a), metisA_port, NULL);
+ metisConfigurationListeners_SetupAll(metisForwarder_GetConfiguration(metis_b), metisB_port, NULL);
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ // ===============================================
+ /* 3) create a tunnel from A->B. */
+
+ // connect from any address
+ struct sockaddr_in metisA_AnyIpAddress;
+ memset(&metisA_AnyIpAddress, 0, sizeof(metisA_AnyIpAddress));
+ metisA_AnyIpAddress.sin_family = PF_INET;
+ metisA_AnyIpAddress.sin_addr.s_addr = INADDR_ANY;
+
+ // connect to 127.0.0.1:10002
+ struct sockaddr_in metisB_LoopbackAddress;
+ memset(&metisB_LoopbackAddress, 0, sizeof(metisB_LoopbackAddress));
+ metisB_LoopbackAddress.sin_family = PF_INET;
+ metisB_LoopbackAddress.sin_port = htons(metisB_port);
+ inet_pton(AF_INET, "127.0.0.1", &(metisB_LoopbackAddress.sin_addr));
+
+ CPIAddress *metisA_localCpiAddress = cpiAddress_CreateFromInet(&metisA_AnyIpAddress);
+ CPIAddress *metisA_remoteCpiAddress = cpiAddress_CreateFromInet(&metisB_LoopbackAddress);
+
+ MetisIoOperations *ops = metisTcpTunnel_Create(metis_a, metisA_localCpiAddress, metisA_remoteCpiAddress);
+ MetisConnection *conn = metisConnection_Create(ops);
+ metisConnectionTable_Add(metisForwarder_GetConnectionTable(metis_a), conn);
+
+ cpiAddress_Destroy(&metisA_localCpiAddress);
+ cpiAddress_Destroy(&metisA_remoteCpiAddress);
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ // ===============================================
+ /* 4) setup route to /foo from a to b */
+
+ CCNxName *ccnxName = ccnxName_CreateFromCString("lci:/2=hello");
+ CPIRouteEntry *route = cpiRouteEntry_Create(ccnxName, ops->getConnectionId(ops), NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ bool success = metisForwarder_AddOrUpdateRoute(metis_a, route);
+ cpiRouteEntry_Destroy(&route);
+ assertTrue(success, "error adding route from A to B");
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ // ===============================================
+ /* 5) Connect client 1 to A */
+
+ struct sockaddr_in metisA_LoopbackAddress;
+ memset(&metisA_LoopbackAddress, 0, sizeof(metisA_LoopbackAddress));
+ metisA_LoopbackAddress.sin_family = PF_INET;
+ metisA_LoopbackAddress.sin_port = htons(metisA_port);
+ inet_pton(AF_INET, "127.0.0.1", &(metisA_LoopbackAddress.sin_addr));
+
+ int client1_Socket = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(client1_Socket < 0, "Error creating socket: (%d) %s", errno, strerror(errno));
+
+ int failure = connect(client1_Socket, (struct sockaddr *) &metisA_LoopbackAddress, sizeof(metisA_LoopbackAddress));
+ assertFalse(failure, "Error connect: (%d) %s", errno, strerror(errno));
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ // ===============================================
+ /* 6) Connect client 2 to B */
+
+ // We need to sniff connections on metis b to learn the connection ID of the client
+
+ int client2_Socket = socket(PF_INET, SOCK_STREAM, 0);
+ assertFalse(client2_Socket < 0, "Error creating socket: (%d) %s", errno, strerror(errno));
+
+ failure = connect(client2_Socket, (struct sockaddr *) &metisB_LoopbackAddress, sizeof(metisB_LoopbackAddress));
+ assertFalse(failure, "Error connect: (%d) %s", errno, strerror(errno));
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ unsigned client2_ConnectionId = metisMissive_GetConnectionId(receiver_b.lastMessage);
+ printf("client 2 connection id is %u\n", client2_ConnectionId);
+
+ // ===============================================
+ /* 7) Setup route to /foo from metis B to client 2. */
+
+ ccnxName = ccnxName_CreateFromCString("lci:/2=hello");
+ route = cpiRouteEntry_Create(ccnxName, client2_ConnectionId, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ success = metisForwarder_AddOrUpdateRoute(metis_b, route);
+ cpiRouteEntry_Destroy(&route);
+ assertTrue(success, "error adding route from B to #2");
+
+ // ---- run
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ // ----
+
+ // ===============================================
+ /* 8) Sent interest from #1 to #2 */
+
+ ssize_t interest_write_length = write(client1_Socket, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertTrue(interest_write_length == sizeof(metisTestDataV0_InterestWithName),
+ "Wrong write length, expected %zu got %zu",
+ sizeof(metisTestDataV0_EncodedInterest),
+ interest_write_length);
+
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+
+ // wait to receive it
+ uint8_t readBuffer[1024];
+ ssize_t interest_read_length = read(client2_Socket, readBuffer, 1024);
+ assertTrue(interest_read_length == sizeof(metisTestDataV0_InterestWithName),
+ "Wrong write length, expected %zu got %zu",
+ sizeof(metisTestDataV0_InterestWithName),
+ interest_read_length);
+
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+
+ // ===============================================
+ /* 9) Send object back from #2 to #1 */
+
+ ssize_t object_write_length = write(client2_Socket, metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject));
+ assertTrue(object_write_length == sizeof(metisTestDataV0_EncodedObject),
+ "Wrong write length, expected %zu got %zu",
+ sizeof(metisTestDataV0_EncodedInterest),
+ object_write_length);
+
+ // very important: run b first, then a
+ metisDispatcher_RunDuration(dispatcher_b, &((struct timeval) { 0, 1000 }));
+ metisDispatcher_RunDuration(dispatcher_a, &((struct timeval) { 0, 1000 }));
+
+ // wait to receive it
+ ssize_t object_read_length = read(client1_Socket, readBuffer, 1024);
+ assertTrue(object_read_length == sizeof(metisTestDataV0_EncodedObject),
+ "Wrong write length, expected %zu got %zu",
+ sizeof(metisTestDataV0_EncodedObject),
+ object_read_length);
+
+
+ // ===============================================
+ // cleanup
+ metisMissive_Release(&receiver_a.lastMessage);
+ metisMissive_Release(&receiver_b.lastMessage);
+ metisMessengerRecipient_Destroy(&recipient_a);
+ metisMessengerRecipient_Destroy(&recipient_b);
+ metisForwarder_Destroy(&metis_b);
+ metisForwarder_Destroy(&metis_a);
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(test_sys_TcpTunnel);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}
diff --git a/metis/ccnx/forwarder/metis/test/test_sys_UdpEndToEnd.c b/metis/ccnx/forwarder/metis/test/test_sys_UdpEndToEnd.c
new file mode 100644
index 00000000..2ce7fdca
--- /dev/null
+++ b/metis/ccnx/forwarder/metis/test/test_sys_UdpEndToEnd.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.
+ */
+
+
+/**
+ * These are end-to-end system tests. They nail up two UDP connections, setup a FIB entry, and send
+ * and interest then a content object back.
+ */
+
+#include <config.h>
+#include <LongBow/unit-test.h>
+#include <parc/algol/parc_SafeMemory.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <ccnx/api/control/cpi_RouteEntry.h>
+#include <ccnx/forwarder/metis/core/metis_Forwarder.h>
+#include <ccnx/forwarder/metis/io/metis_UdpListener.h>
+
+#include <ccnx/forwarder/metis/testdata/metis_TestDataV0.h>
+
+LONGBOW_TEST_RUNNER(sys_UdpEndToEnd)
+{
+ // 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(sys_UdpEndToEnd)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// The Test Runner calls this function once after all the Test Fixtures are run.
+LONGBOW_TEST_RUNNER_TEARDOWN(sys_UdpEndToEnd)
+{
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+// ---- Used to monitor Missive messages so we know when
+// a connection is setup
+static struct test_notifier_data {
+ MetisMissiveType type;
+ unsigned connectionid;
+} testNotifierData;
+
+static void
+testNotifier(MetisMessengerRecipient *recipient, MetisMissive *missive)
+{
+ struct test_notifier_data *data = metisMessengerRecipient_GetRecipientContext(recipient);
+ data->type = metisMissive_GetType(missive);
+ data->connectionid = metisMissive_GetConnectionId(missive);
+ metisMissive_Release(&missive);
+}
+
+// ---- Utility functions to setup TCP endpoints
+
+static void
+setupInetListener(MetisForwarder *metis, uint16_t port)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ MetisListenerSet *listeners = metisForwarder_GetListenerSet(metis);
+ MetisListenerOps *ops = metisUdpListener_CreateInet(metis, addr);
+ metisListenerSet_Add(listeners, ops);
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+}
+
+static int
+setupInetClient(MetisForwarder *metis, uint16_t port)
+{
+ struct sockaddr_in addr;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(port);
+ inet_pton(AF_INET, "127.0.0.1", &(addr.sin_addr));
+
+ int fd = socket(PF_INET, SOCK_DGRAM, 0);
+ assertFalse(fd < 0, "Error on socket: (%d) %s", errno, strerror(errno));
+
+ int failure = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
+ assertFalse(failure, "Error on connect: (%d) %s", errno, strerror(errno));
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(metis), &((struct timeval) { 0, 10000 }));
+ return fd;
+}
+
+// ---- Global state used by tests
+
+static struct global_state {
+ MetisForwarder *metis;
+ MetisMessengerRecipient *recipient;
+
+ int fd_sender;
+ int fd_receiver;
+} globalState;
+
+static void
+setupUdp(void)
+{
+ globalState.metis = metisForwarder_Create(NULL);
+ globalState.recipient = metisMessengerRecipient_Create(&testNotifierData, testNotifier);
+
+ // register a messenger callback so we know when the connections get setup
+ MetisMessenger *messenger = metisForwarder_GetMessenger(globalState.metis);
+ metisMessenger_Register(messenger, globalState.recipient);
+
+ setupInetListener(globalState.metis, 49996);
+ setupInetListener(globalState.metis, 49997);
+
+ // create two test connections and learn their connection IDs via
+ // the messenger callback
+
+ globalState.fd_sender = setupInetClient(globalState.metis, 49996);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 10000 }));
+
+ printf("sender connection id = %u\n", testNotifierData.connectionid);
+
+ globalState.fd_receiver = setupInetClient(globalState.metis, 49997);
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 10000 }));
+
+ // send something to actually connect it
+ ssize_t nwritten = write(globalState.fd_receiver, metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject));
+ assertTrue(nwritten == sizeof(metisTestDataV0_EncodedObject), "Error on write, expected %zu got %zd", sizeof(metisTestDataV0_EncodedObject), nwritten);
+
+ // crank the handle
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 10000 }));
+
+ unsigned receiverConnectionId = testNotifierData.connectionid;
+ printf("receiver connection id = %u\n", testNotifierData.connectionid);
+
+ // Add a FIB entry out the receiver connection
+ CCNxName *ccnxNameToAdd = ccnxName_CreateFromCString("lci:/2=hello/0xF000=ouch");
+ CPIRouteEntry *routeAdd = cpiRouteEntry_Create(ccnxNameToAdd, receiverConnectionId, NULL, cpiNameRouteProtocolType_STATIC, cpiNameRouteType_LONGEST_MATCH, NULL, 1);
+ metisForwarder_AddOrUpdateRoute(globalState.metis, routeAdd);
+ cpiRouteEntry_Destroy(&routeAdd);
+}
+
+static void
+teardownUdp(void)
+{
+ metisForwarder_Destroy(&globalState.metis);
+ metisMessengerRecipient_Destroy(&globalState.recipient);
+
+ close(globalState.fd_sender);
+ close(globalState.fd_receiver);
+}
+
+// ==========================================================
+
+LONGBOW_TEST_FIXTURE(Global)
+{
+ //XXX: these tests do not work anymore because we do not create the UDP connection from the listener
+ //LONGBOW_RUN_TEST_CASE(Global, passInterest);
+ //LONGBOW_RUN_TEST_CASE(Global, returnContentObject);
+}
+
+LONGBOW_TEST_FIXTURE_SETUP(Global)
+{
+ setupUdp();
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_FIXTURE_TEARDOWN(Global)
+{
+ teardownUdp();
+ if (parcSafeMemory_ReportAllocation(STDOUT_FILENO) != 0) {
+ return LONGBOW_STATUS_MEMORYLEAK;
+ }
+ return LONGBOW_STATUS_SUCCEEDED;
+}
+
+LONGBOW_TEST_CASE(Global, passInterest)
+{
+ uint8_t receiveBuffer[1024];
+
+ // now send the interest on the sender and see if we get it on the receiver
+ ssize_t write_length = write(globalState.fd_sender, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName), "Partial write, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), write_length, errno, strerror(errno));
+
+ // run for a duration so there is time to read the message, pass it off to the handler, then send the message out
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 10000 }));
+
+ ssize_t read_length = read(globalState.fd_receiver, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_InterestWithName), "Incorrect read, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), read_length, errno, strerror(errno));
+
+ // We decrement the hoplimit, so need a new truth value
+ uint8_t truth[1024];
+ memcpy(truth, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ truth[12] = 31;
+
+ assertTrue(memcmp(receiveBuffer, truth, read_length) == 0, "Messages do not match");
+}
+
+LONGBOW_TEST_CASE(Global, returnContentObject)
+{
+ uint8_t receiveBuffer[1024];
+
+ // send the interest on the sender and see if we get it on the receiver
+ ssize_t write_length = write(globalState.fd_sender, metisTestDataV0_InterestWithName, sizeof(metisTestDataV0_InterestWithName));
+ assertTrue(write_length == sizeof(metisTestDataV0_InterestWithName), "Partial write of interest, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), write_length, errno, strerror(errno));
+
+ // run for a duration so there is time to read the message, pass it off to the handler, then send the message out
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 1000 }));
+
+ ssize_t read_length = read(globalState.fd_receiver, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_InterestWithName), "Incorrect read of interest, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_InterestWithName), read_length, errno, strerror(errno));
+
+ // send content object back
+ write_length = write(globalState.fd_receiver, metisTestDataV0_EncodedObject, sizeof(metisTestDataV0_EncodedObject));
+ assertTrue(write_length == sizeof(metisTestDataV0_EncodedObject), "Partial write of object, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_EncodedObject), write_length, errno, strerror(errno));
+
+ metisDispatcher_RunDuration(metisForwarder_GetDispatcher(globalState.metis), &((struct timeval) { 0, 1000 }));
+
+ // now check that we got the object
+ read_length = read(globalState.fd_sender, receiveBuffer, 1024);
+ assertTrue(read_length == sizeof(metisTestDataV0_EncodedObject), "Incorrect read of object, expected %zd got %zd: (%d) %s", sizeof(metisTestDataV0_EncodedObject), read_length, errno, strerror(errno));
+
+ assertTrue(memcmp(receiveBuffer, metisTestDataV0_EncodedObject, read_length) == 0, "Objects do not match");
+}
+
+int
+main(int argc, char *argv[])
+{
+ LongBowRunner *testRunner = LONGBOW_TEST_RUNNER_CREATE(sys_UdpEndToEnd);
+ int exitStatus = longBowMain(argc, argv, testRunner, NULL);
+ longBowTestRunner_Destroy(&testRunner);
+ exit(exitStatus);
+}