diff options
author | michele papalini <micpapal+fdio@cisco.com> | 2017-02-23 17:01:34 +0100 |
---|---|---|
committer | Michele Papalini <micpapal+fdio@cisco.com> | 2017-02-23 17:23:19 +0000 |
commit | c580a00aac271a524e5a75b35f4b91c174ed227b (patch) | |
tree | feddc15a9f1a4eb319d950f8d6330ac2b91e3d99 /metis/ccnx/forwarder/metis/test | |
parent | 9b30fc10fb1cbebe651e5a107e8ca5b24de54675 (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.txt | 16 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/test/test_sys_Errors.c | 293 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/test/test_sys_EtherEndToEnd.c | 347 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/test/test_sys_TcpEndToEnd.c | 249 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/test/test_sys_TcpTunnel.c | 298 | ||||
-rw-r--r-- | metis/ccnx/forwarder/metis/test/test_sys_UdpEndToEnd.c | 255 |
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); +} |