예제 #1
0
static CCNxMetaMessage *
_PIT_Command(Athena *athena, CCNxInterest *interest)
{
    CCNxMetaMessage *responseMessage;
    responseMessage = athenaPIT_ProcessMessage(athena->athenaPIT, interest);
    if (responseMessage) {
        return responseMessage;
    }

    CCNxName *ccnxName = ccnxInterest_GetName(interest);
    if (ccnxName_GetSegmentCount(ccnxName) > AthenaCommandSegment) {
        CCNxNameSegment *nameSegment = ccnxName_GetSegment(ccnxName, AthenaCommandSegment);
        char *command = ccnxNameSegment_ToString(nameSegment);

        if (strcasecmp(command, AthenaCommand_List) == 0) {
            parcLog_Debug(athena->log, "PIT List command invoked");
            PARCList *pitEntries = athenaPIT_CreateEntryList(athena->athenaPIT);
            printf("\n");
            for (size_t i = 0; i < parcList_Size(pitEntries); ++i) {
                PARCBuffer *strbuf = parcList_GetAtIndex(pitEntries, i);
                char *toprint = parcBuffer_ToString(strbuf);
                parcLog_Info(athena->log, "%s\n", toprint);
                parcMemory_Deallocate(&toprint);
            }
            parcList_Release(&pitEntries);
            responseMessage = _create_response(athena, ccnxName, "PIT listed on forwarder output log.");
        } else {
            responseMessage = _create_response(athena, ccnxName, "Unknown command: %s", command);
        }

        parcMemory_Deallocate(&command);
    }
    return responseMessage;
}
예제 #2
0
LONGBOW_TEST_CASE(Global, parcLog_Info)
{
    PARCLog *log = (PARCLog *) longBowTestCase_GetClipBoardData(testCase);

    assertTrue(parcLog_Info(log, "This is a info message"),
               "Expected message to be logged successfully");
}
static void
_TemplateClose(AthenaTransportLink *athenaTransportLink)
{
    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "link %s closed", athenaTransportLink_GetName(athenaTransportLink));
    _TemplateLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    _TemplateLinkData_Destroy(&linkData);
}
예제 #4
0
LONGBOW_TEST_CASE(Global, parcLog_Info_WrongLevel)
{
    PARCLog *log = (PARCLog *) longBowTestCase_GetClipBoardData(testCase);
    parcLog_SetLevel(log, PARCLogLevel_Off);

    assertFalse(parcLog_Info(log, "This is a debug message"),
                "Expected message to not be logged");
}
예제 #5
0
PARCBuffer *
athenaEthernet_Receive(AthenaEthernet *athenaEthernet, int timeout, AthenaTransportLinkEvent *events)
{
    // Allocate, and read, a new BPF buffer if no packets are currently pending in an old one
    if (athenaEthernet->bpfBuffer == NULL) {
        athenaEthernet->bpfBuffer = parcBuffer_Allocate(athenaEthernet->etherBufferLength);
        uint8_t *buffer = parcBuffer_Overlay(athenaEthernet->bpfBuffer, 0);

        athenaEthernet->readCount = read(athenaEthernet->fd, buffer, athenaEthernet->etherBufferLength);
        if (athenaEthernet->readCount == -1) {
            if ((errno == EAGAIN) || (errno == EINTR)) {
                parcLog_Info(athenaEthernet->log, "Ethernet read retry");
                return NULL;
            }
            parcLog_Error(athenaEthernet->log, "recv: %s", strerror(errno));
            *events = AthenaTransportLinkEvent_Error;
            parcBuffer_Release(&athenaEthernet->bpfBuffer);
            return NULL;
        }
        parcLog_Debug(athenaEthernet->log, "received bpf packet (size=%d)", athenaEthernet->readCount);
    }

    // Obtain the current position in the BPF buffer to return a message from
    size_t position = parcBuffer_Position(athenaEthernet->bpfBuffer);

    // Read the BPF header and seek past it
    struct bpf_hdr *bpfhdr = parcBuffer_Overlay(athenaEthernet->bpfBuffer, sizeof(struct bpf_hdr));
    parcBuffer_SetLimit(athenaEthernet->bpfBuffer, position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen);
    parcBuffer_SetPosition(athenaEthernet->bpfBuffer, position + bpfhdr->bh_hdrlen);
    parcLog_Debug(athenaEthernet->log, "received message (size=%d)", bpfhdr->bh_datalen);

    // Slice a new PARCBuffer with the message to send up.
    PARCBuffer *wireFormatBuffer = parcBuffer_Slice(athenaEthernet->bpfBuffer);

    // If there's another packet in the buffer, position it and flag a receive event
    if ((athenaEthernet->readCount - (position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen)) != 0) {
        parcBuffer_SetLimit(athenaEthernet->bpfBuffer, athenaEthernet->readCount);
        parcBuffer_SetPosition(athenaEthernet->bpfBuffer,
                               BPF_WORDALIGN(position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen));
        // Mark a receive event for this packet
        *events = AthenaTransportLinkEvent_Receive;
    } else {
        parcBuffer_Release(&athenaEthernet->bpfBuffer);
    }

    return wireFormatBuffer;
}
예제 #6
0
static AthenaTransportLink *
_newLink(AthenaTransportLink *athenaTransportLink, _UDPLinkData *newLinkData)
{
    // Accept a new tunnel connection.

    // Clone a new link from the current listener.
    const char *derivedLinkName = _createNameFromLinkData(&newLinkData->link);
    AthenaTransportLink *newTransportLink = athenaTransportLink_Clone(athenaTransportLink,
                                                                      derivedLinkName,
                                                                      _UDPSend,
                                                                      _UDPReceiveProxy,
                                                                      _UDPClose);
    if (newTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Clone failed");
        parcMemory_Deallocate(&derivedLinkName);
        _UDPLinkData_Destroy(&newLinkData);
        return NULL;
    }

    _setConnectLinkState(newTransportLink, newLinkData);

    // Send the new link up to be added.
    int result = athenaTransportLink_AddLink(athenaTransportLink, newTransportLink);
    if (result == -1) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_AddLink failed: %s", strerror(errno));
        _UDPLinkData_Destroy(&newLinkData);
        athenaTransportLink_Release(&newTransportLink);
    } else {
        parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                     "new link accepted by %s: %s %s",
                     athenaTransportLink_GetName(athenaTransportLink), derivedLinkName,
                     athenaTransportLink_IsNotLocal(athenaTransportLink) ? "" : "(Local)");
    }

    parcMemory_Deallocate(&derivedLinkName);

    // Could pass a message back here regarding the new link.
    return newTransportLink;
}
예제 #7
0
static bool
_linkIsEOF(AthenaTransportLink *athenaTransportLink)
{
    struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    // If poll indicates there's a read event and a subsequent read returns zero our peer has hungup.
    struct pollfd pollfd = { .fd = linkData->fd, .events = POLLIN };
    int events = poll(&pollfd, 1, 0);
    if (events == -1) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "poll error (%s)", strerror(errno));
        return true; // poll error, close the link
    } else if (events == 0) {
        // there are no pending events, was truly a zero read
        return false;
    }
    if (pollfd.revents & POLLIN) {
        char peekBuffer;
        ssize_t readCount = recv(linkData->fd, (void *) &peekBuffer, 1, MSG_PEEK);
        if (readCount == -1) {
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // read blocked
                linkData->_stats.receive_ReadWouldBlock++;
                return false;
            }
            return true; // read error
        }
        if (readCount == 0) { // EOF
            return true;
        }
    }
    return false;
}

static void
_UDPClose(AthenaTransportLink *athenaTransportLink)
{
    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "link %s closed", athenaTransportLink_GetName(athenaTransportLink));
    _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    close(linkData->fd);
    _UDPLinkData_Destroy(&linkData);
}
예제 #8
0
static CCNxMetaMessage *
_create_response(Athena *athena, CCNxName *ccnxName, const char *format, ...)
{
    va_list ap;
    va_start(ap, format);

    char responseBuffer[MAXPATHLEN];
    vsprintf(responseBuffer, format, ap);

    PARCBuffer *responsePayload = parcBuffer_AllocateCString(responseBuffer);

    parcLog_Info(athena->log, responseBuffer);
    CCNxContentObject *responseContent = ccnxContentObject_CreateWithNameAndPayload(ccnxName, responsePayload);
    CCNxMetaMessage *responseMessage = ccnxMetaMessage_CreateFromContentObject(responseContent);

    ccnxContentObject_Release(&responseContent);
    parcBuffer_Release(&responsePayload);

    athena_EncodeMessage(responseMessage);
    return responseMessage;
}
예제 #9
0
//
// Open a listener which will create new links when messages arrive and queue them appropriately.
// Listeners are inherently insecure, as an adversary could easily create many connections that are never closed.
//
static AthenaTransportLink *
_UDPOpenListener(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, struct sockaddr_in *destination, size_t mtu)
{
    const char *derivedLinkName;

    _UDPLinkData *linkData = _UDPLinkData_Create();
    linkData->multiplexTable = parcHashCodeTable_Create(_connectionEquals, _connectionHashCode, NULL, _closeConnection);
    assertNotNull(linkData->multiplexTable, "Could not create multiplex table for new listener");

    linkData->link.myAddress = *destination;
    linkData->link.myAddressLength = sizeof(struct sockaddr_in);
    linkData->link.mtu = mtu;

    linkData->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (linkData->fd < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "socket error (%s)", strerror(errno));
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    int result = _setSocketOptions(athenaTransportLinkModule, linkData->fd);
    if (result) {
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // Set non-blocking flag
    int flags = fcntl(linkData->fd, F_GETFL, NULL);
    if (flags < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "fcntl failed to get non-blocking flag (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }
    result = fcntl(linkData->fd, F_SETFL, flags | O_NONBLOCK);
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "fcntl failed to set non-blocking flag (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // bind to listen on requested address
    result = bind(linkData->fd, (struct sockaddr *) &linkData->link.myAddress, linkData->link.myAddressLength);
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "bind error (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    // Listener doesn't require a send method.  The receive method is used to establish new connections.
    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          NULL,
                                                                          _UDPReceiveListener,
                                                                          _UDPClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEventFd(athenaTransportLink, linkData->fd);

    // Links established for listening are not used to route messages.
    // They can be kept in a listener list that doesn't consume a linkId.
    athenaTransportLink_SetRoutable(athenaTransportLink, false);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new listener established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
}
예제 #10
0
//
// Open a UDP point to point connection.
//
static AthenaTransportLink *
_UDPOpenConnection(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, struct sockaddr_in *source, struct sockaddr_in *destination, size_t mtu)
{
    const char *derivedLinkName;

    _UDPLinkData *linkData = _UDPLinkData_Create();

    linkData->link.peerAddress = *((struct sockaddr_in *) destination);
    linkData->link.peerAddressLength = sizeof(struct sockaddr_in);
    linkData->link.mtu = mtu;

    linkData->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (linkData->fd < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule), "socket error (%s)", strerror(errno));
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    int result = _setSocketOptions(athenaTransportLinkModule, linkData->fd);
    if (result) {
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // bind the local endpoint so we can know our allocated port if it was wildcarded
    result = bind(linkData->fd, (struct sockaddr *) source, sizeof(struct sockaddr_in));
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "bind error (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // Retrieve the local endpoint data, used to create the derived name.
    linkData->link.myAddressLength = sizeof(struct sockaddr_in);
    result = getsockname(linkData->fd, (struct sockaddr *) &linkData->link.myAddress, &linkData->link.myAddressLength);
    if (result != 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Failed to obtain endpoint information from getsockname.");
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _UDPSend,
                                                                          _UDPReceive,
                                                                          _UDPClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    _setConnectLinkState(athenaTransportLink, linkData);
    // Enable Send? XXX
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
    return NULL;
}
static AthenaTransportLink *
_TemplateOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    // Parse the URI contents to determine the link specific parameters
    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }

    //
    // This template link module doesn't use the authority fields.
    // The access methods are here for use by derived link modules, if needed.
    //
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    //const char *URIAddress = parcURIAuthority_GetHostName(authority);
    //in_port_t port = parcURIAuthority_GetPort(authority);
    parcURIAuthority_Release(&authority);

    int forceLocal = 0;
    char specifiedLinkName[MAXPATHLEN] = { 0 };
    const char *linkName = NULL;

    // Parse connection segment parameters, Name and Local
    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (_parseLinkName(token, specifiedLinkName) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = specifiedLinkName;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            forceLocal = _parseLocalFlag(token);
            if (forceLocal == 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    _TemplateLinkData *linkData = _TemplateLinkData_Create();

    const char *derivedLinkName = _createNameFromLinkData(linkData);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _TemplateSend,
                                                                          _TemplateReceive,
                                                                          _TemplateClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _TemplateLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);

    // forced IsLocal/IsNotLocal, mainly for testing
    if (athenaTransportLink && forceLocal) {
        athenaTransportLink_ForceLocal(athenaTransportLink, forceLocal);
    }

    return athenaTransportLink;
}
예제 #12
0
/**
 * Run the consumer to fetch the specified file. Save it to disk once transferred.
 *
 * @param [in] target Name of the content to request.
 * @param [in] outFile Name of the file to which the buffer will be written.
 */
static int
_ccnxFileRepoClient_Run(char *target, char *outFile)
{
    parcSecurity_Init();

    PARCLog *log = _ccnxFileRepoClient_CreateLogger();

    CCNxPortalFactory *factory = _setupConsumerPortalFactory();
    CCNxPortal *portal = ccnxPortalFactory_CreatePortal(factory, ccnxPortalRTA_Message);
    assertNotNull(portal, "Expected a non-null CCNxPortal pointer.");

    CCNxName *name = ccnxName_CreateFromCString(target);
    CCNxInterest *interest = ccnxInterest_CreateSimple(name);
    CCNxMetaMessage *message = ccnxMetaMessage_CreateFromInterest(interest);

    if (ccnxPortal_Send(portal, message, CCNxStackTimeout_Never)) {
        while (ccnxPortal_IsError(portal) == false) {
            CCNxMetaMessage *response = ccnxPortal_Receive(portal, CCNxStackTimeout_Never);
            if (response != NULL) {
                if (ccnxMetaMessage_IsManifest(response)) {
                    parcLog_Info(log, "Received root manifest. Beginning to retrieve the content.");

                    // Extract the manifest and instantiate a new fetcher for it
                    CCNxManifest *root = ccnxMetaMessage_GetManifest(response);
                    CCNxFileRepoManifestFetcher *fetcher = ccnxFileRepoManifestFetcher_Create(portal, root);

                    // Initialize the file offset and I/O buffer
                    size_t fileOffset = 0;
                    PARCBuffer *chunkBuffer = parcBuffer_Allocate(ccnxFileRepoCommon_ClientBufferSize);

                    // Start reading from the manifest until done
                    bool done = false;
                    while (!done) {
                        // Reset the buffer information
                        parcBuffer_SetPosition(chunkBuffer, 0);
                        parcBuffer_SetLimit(chunkBuffer, ccnxFileRepoCommon_ClientBufferSize);

                        // Fill the buffer with data from the manifest
                        done = ccnxFileRepoManifestFetcher_FillBuffer(fetcher, chunkBuffer);
                        parcBuffer_Flip(chunkBuffer);

                        // Write the buffer to the file
                        size_t totalSize = parcBuffer_Remaining(chunkBuffer);
                        _ccnxFileRepoClient_AppendBufferToFile(outFile, chunkBuffer, fileOffset);
                        fileOffset += totalSize;

                        // Flip the buffer back around for writing
                        parcBuffer_Flip(chunkBuffer);
                    }
                    parcBuffer_Release(&chunkBuffer);

                    break;
                } else if (ccnxMetaMessage_IsContentObject(response)) {
                    parcLog_Info(log, "Received a content object. Dump the payload and exit.");
                    CCNxContentObject *contentObject = ccnxMetaMessage_GetContentObject(response);
                    PARCBuffer *payload = ccnxContentObject_GetPayload(contentObject);
                    _ccnxFileRepoClient_AppendBufferToFile(outFile, payload, 0);
                    break;
                }
            }
            ccnxMetaMessage_Release(&response);
        }
    }

    ccnxPortal_Release(&portal);
    ccnxPortalFactory_Release(&factory);

    parcSecurity_Fini();
    return 0;
}
//
// Open a listener which will create new links when messages arrive and queue them appropriately.
// Listeners are inherently insecure, as an adversary could easily create many connections that are never closed.
//
static AthenaTransportLink *
_ETHOpenListener(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, const char *device, struct ether_addr *source, size_t mtu)
{
    const char *derivedLinkName;

    _ETHLinkData *linkData = _ETHLinkData_Create();
    linkData->multiplexTable = parcHashCodeTable_Create(_connectionEquals, _connectionHashCode, NULL, _closeConnection);
    assertNotNull(linkData->multiplexTable, "Could not create multiplex table for new listener");

    linkData->athenaEthernet = athenaEthernet_Create(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                                                     device, CCNX_ETHERTYPE);
    if (linkData->athenaEthernet == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Use specified source MAC address, or default to device MAC
    if (source) {
        memcpy(&(linkData->link.myAddress), source, sizeof(struct ether_addr));
    } else {
        athenaEthernet_GetMAC(linkData->athenaEthernet, &(linkData->link.myAddress));
    }
    linkData->link.myAddressLength = ETHER_ADDR_LEN;

    if (linkData->athenaEthernet == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "athenaEthernet_Create error");
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link, true);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    // Listener doesn't require a send method.  The receive method is used to establish new connections.
    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          NULL,
                                                                          _ETHReceiveListener,
                                                                          _ETHClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _ETHLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEventFd(athenaTransportLink, athenaEthernet_GetDescriptor(linkData->athenaEthernet));

    // Links established for listening are not used to route messages.
    // They can be kept in a listener list that doesn't consume a linkId.
    athenaTransportLink_SetRoutable(athenaTransportLink, false);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new listener established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
}
//
// Open a point to point connection.
//
static AthenaTransportLink *
_ETHOpenConnection(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, const char *device, struct ether_addr *source, struct ether_addr *destination, size_t mtu)
{
    const char *derivedLinkName;

    _ETHLinkData *linkData = _ETHLinkData_Create();

    linkData->athenaEthernet = athenaEthernet_Create(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                                                     device, CCNX_ETHERTYPE);
    if (linkData->athenaEthernet == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Use our default MAC address if none specified.
    if (source == NULL) {
        athenaEthernet_GetMAC(linkData->athenaEthernet, &(linkData->link.myAddress));
        linkData->link.myAddressLength = ETHER_ADDR_LEN;
    } else {
        memcpy(&(linkData->link.myAddress), source, sizeof(struct ether_addr));
    }

    // If there's no destination specified, drop the request.
    if (destination == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Copy the peer destination address into our link data
    memcpy(&(linkData->link.peerAddress), destination, sizeof(struct ether_addr));

    derivedLinkName = _createNameFromLinkData(&linkData->link, false);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _ETHSend,
                                                                          _ETHReceive,
                                                                          _ETHClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    _setConnectLinkState(athenaTransportLink, linkData);

    // Enable Sends
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
    return NULL;
}
static int
_ETHSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage)
{
    struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }

    // Construct our header to prepend
    struct ether_header header;
    memcpy(header.ether_shost, &(linkData->link.myAddress), ETHER_ADDR_LEN * sizeof(uint8_t));
    memcpy(header.ether_dhost, &(linkData->link.peerAddress), ETHER_ADDR_LEN * sizeof(uint8_t));
    header.ether_type = htons(athenaEthernet_GetEtherType(linkData->athenaEthernet));

    // An iovec to contain the header and packet data
    struct iovec iov[2];
    struct iovec *array = iov;
    int iovcnt = 2;
    size_t messageLength = 0;

    // If the iovec we're prepending to has more than one element, allocatedIovec holds the
    // allocated IO vector of the right size that we must deallocate before returning.
    struct iovec *allocatedIovec = NULL;

    // Attach the header and populate the iovec

    CCNxCodecNetworkBufferIoVec *iovec = athenaTransportLinkModule_GetMessageIoVector(ccnxMetaMessage);

    iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec);
    const struct iovec *networkBufferIovec = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec);

    // Trivial case, single iovec element.
    if (iovcnt == 1) {
        // Header
        array[0].iov_len = sizeof(struct ether_header);
        array[0].iov_base = &header;

        // Message content
        array[1].iov_len = networkBufferIovec->iov_len;
        array[1].iov_base = networkBufferIovec->iov_base;
        messageLength = array[0].iov_len + array[1].iov_len;
    } else {
        // Allocate a new iovec if more than one vector
        allocatedIovec = parcMemory_Allocate(sizeof(struct iovec) * (iovcnt + 1));
        array = allocatedIovec;

        // Header
        array[0].iov_len = sizeof(struct ether_header);
        array[0].iov_base = &header;
        messageLength = array[0].iov_len;

        // Append message content
        for (int i = 0; i < iovcnt; i++) {
            array[i + 1].iov_len = networkBufferIovec[i].iov_len;
            array[i + 1].iov_base = networkBufferIovec[i].iov_base;
            messageLength += array[i + 1].iov_len;
        }
    }
    iovcnt++; // increment for the header

    if (linkData->link.mtu) {
        if (messageLength > linkData->link.mtu) {
            if (allocatedIovec != NULL) {
                parcMemory_Deallocate(&allocatedIovec);
            }
            ccnxCodecNetworkBufferIoVec_Release(&iovec);
            errno = EMSGSIZE;
            return -1;
        }
    }

    parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                  "sending message (size=%d)", messageLength);

    ssize_t writeCount = 0;
    writeCount = athenaEthernet_Send(linkData->athenaEthernet, array, iovcnt);
    ccnxCodecNetworkBufferIoVec_Release(&iovec);

    // Free up any storage allocated for a non-singular iovec
    if (allocatedIovec != NULL) {
        parcMemory_Deallocate(&allocatedIovec);
        array = NULL;
    }

    // on error close the link, else return to retry a zero write
    if (writeCount == -1) {
        if ((errno == EAGAIN) || (errno == EINTR)) {
            parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink), "send retry");
            linkData->_stats.send_Retry++;
            return -1;
        }
        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);

        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "send error (%s)", strerror(errno));
        linkData->_stats.send_Error++;
        return -1;
    }

    // Short write
    if (writeCount != messageLength) {
        linkData->_stats.send_ShortWrite++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write");
        return -1;
    }

    return 0;
}