Пример #1
0
/**
 * Common parts of setting up a MetisTlvName after the backing memory has been allocated and copied in to.
 *
 * PRECONDITIONS: name->memory and name->memoryLength set
 */
static void
_setup(MetisTlvName *name)
{
    name->refCountPtr = parcMemory_Allocate(sizeof(unsigned));
    assertNotNull(name->refCountPtr, "parcMemory_Allocate(%zu) returned NULL", sizeof(unsigned));
    *name->refCountPtr = 1;

    metisTlv_NameSegments(name->memory, name->memoryLength, &name->segmentArray, &name->segmentArrayLength);

    name->segmentCumulativeHashArray = parcMemory_Allocate(name->segmentArrayLength * sizeof(uint32_t));
    assertNotNull(name->segmentCumulativeHashArray, "parcMemory_Allocate(%zu) returned NULL", name->segmentArrayLength * sizeof(uint32_t));

    name->segmentCumulativeHashArrayLengthPtr = parcMemory_Allocate(sizeof(size_t));
    assertNotNull(name->segmentCumulativeHashArrayLengthPtr, "parcMemory_Allocate(%zu) returned NULL", sizeof(size_t));

    *name->segmentCumulativeHashArrayLengthPtr = 1;
    name->segmentCumulativeHashArrayLimit = name->segmentArrayLength;


    if (name->segmentArrayLength > 0) {
        // always hash the 1st name component.  This is needed as the initial case
        // to do the cumulative hashes in metisTlvName_HashCode
        name->segmentCumulativeHashArray[0] = parcHash32_Data(&name->memory[name->segmentArray[0].offset], name->segmentArray[0].length);
    }
}
Пример #2
0
TransportContext *
Transport_Create(TransportTypes type)
{
    if (the_context == NULL) {
        switch (type) {
            case TRANSPORT_RTA:
                the_context = parcMemory_Allocate(sizeof(TransportContext));
                assertNotNull(the_context, "TransportContext could not be allocated, parcMemory_Allocate(%zu) returned NULL", sizeof(TransportContext));

                the_context->references = 0;
                the_context->ops = rta_ops;
                the_context->transport_data = the_context->ops.Create();
                the_context->transport_type = type;
                break;

            default:
                fprintf(stderr, "%s unknown transport type %d\n", __func__, type);
                abort();
                break;
        }
    }

    if (the_context->transport_type == type) {
        the_context->references++;
        return the_context;
    }

    fprintf(stderr, "%s transport type %d not of request type %d\n",
            __func__, the_context->transport_type, type);
    abort();
}
Пример #3
0
/**
 * Given a CCNxName, a directory path, a file name, and a requested chunk number, return a new CCNxContentObject
 * with that CCNxName and containing the specified chunk of the file. The new CCNxContentObject will also
 * contain the number of the last chunk required to transfer the complete file. Note that the last chunk of the
 * file being retrieved is calculated each time we retrieve a chunk so the file can be growing in size as we
 * transfer it.
 * The new CCnxContentObject must eventually be released by calling ccnxContentObject_Release().
 *
 * @param [in] name The CCNxName to use when creating the new CCNxContentObject.
 * @param [in] directoryPath The directory in which to find the specified file.
 * @param [in] fileName The name of the file.
 * @param [in] requestedChunkNumber The number of the requested chunk from the file.
 *
 * @return A new CCNxContentObject instance containing the request chunk of the specified file, or NULL if
 *         the file did not exist or was otherwise unavailable.
 */
static CCNxContentObject *
_createFetchResponse(const CCNxName *name, const char *directoryPath, const char *fileName, uint64_t requestedChunkNumber)
{
    CCNxContentObject *result = NULL;
    uint64_t finalChunkNumber = 0;

    // Combine the directoryPath and fileName into the full path name of the desired file
    size_t filePathBufferSize = strlen(fileName) + strlen(directoryPath) + 2; // +2 for '/' and trailing null.
    char *fullFilePath = parcMemory_Allocate(filePathBufferSize);
    assertNotNull(fullFilePath, "parcMemory_Allocate(%zu) returned NULL", filePathBufferSize);
    snprintf(fullFilePath, filePathBufferSize, "%s/%s", directoryPath, fileName);

    // Make sure the file exists and is accessible before creating a ContentObject response.
    if (tutorialFileIO_IsFileAvailable(fullFilePath)) {
        // Since the file's length can change (e.g. if it is being written to while we're fetching
        // it), the final chunk number can change between requests for content chunks. So, update
        // it each time this function is called.
        finalChunkNumber = _getFinalChunkNumberOfFile(fullFilePath, tutorialCommon_ChunkSize);

        // Get the actual contents of the specified chunk of the file.
        PARCBuffer *payload = tutorialFileIO_GetFileChunk(fullFilePath, tutorialCommon_ChunkSize, requestedChunkNumber);

        if (payload != NULL) {
            result = _createContentObject(name, payload, finalChunkNumber);
            parcBuffer_Release(&payload);
        }
    }

    parcMemory_Deallocate((void **) &fullFilePath);

    return result; // Could be NULL if there was no payload
}
Пример #4
0
static PARCSignature *
_SignDigest(PARCPublicKeySigner *signer, const PARCCryptoHash *digestToSign)
{
    parcSecurity_AssertIsInitialized();

    assertNotNull(signer, "Parameter must be non-null CCNxFileKeystore");
    assertNotNull(digestToSign, "Buffer to sign must not be null");

    // TODO: what is the best way to expose this?
    PARCKeyStore *keyStore = signer->keyStore;
    PARCBuffer *privateKeyBuffer = parcKeyStore_GetDEREncodedPrivateKey(keyStore);
    EVP_PKEY *privateKey = NULL;
    size_t keySize = parcBuffer_Remaining(privateKeyBuffer);
    uint8_t *bytes = parcBuffer_Overlay(privateKeyBuffer, keySize);
    privateKey = d2i_PrivateKey(EVP_PKEY_RSA, &privateKey, (const unsigned char **) &bytes, keySize);
    parcBuffer_Release(&privateKeyBuffer);

    RSA *rsa = EVP_PKEY_get1_RSA(privateKey);

    int opensslDigestType;

    switch (parcCryptoHash_GetDigestType(digestToSign)) {
        case PARCCryptoHashType_SHA256:
            opensslDigestType = NID_sha256;
            break;
        case PARCCryptoHashType_SHA512:
            opensslDigestType = NID_sha512;
            break;
        default:
            trapUnexpectedState("Unknown digest type: %s",
                                parcCryptoHashType_ToString(parcCryptoHash_GetDigestType(digestToSign)));
    }

    uint8_t *sig = parcMemory_Allocate(RSA_size(rsa));
    assertNotNull(sig, "parcMemory_Allocate(%u) returned NULL", RSA_size(rsa));

    unsigned sigLength = 0;
    PARCBuffer *bb_digest = parcCryptoHash_GetDigest(digestToSign);
    int result = RSA_sign(opensslDigestType,
                          (unsigned char *) parcByteArray_Array(parcBuffer_Array(bb_digest)),
                          (int) parcBuffer_Remaining(bb_digest),
                          sig,
                          &sigLength,
                          rsa);
    assertTrue(result == 1, "Got error from RSA_sign: %d", result);
    RSA_free(rsa);

    PARCBuffer *bbSign = parcBuffer_Allocate(sigLength);
    parcBuffer_Flip(parcBuffer_PutArray(bbSign, sigLength, sig));
    parcMemory_Deallocate((void **) &sig);

    PARCSignature *signature =
    parcSignature_Create(_GetSigningAlgorithm(signer),
                         parcCryptoHash_GetDigestType(digestToSign),
                         bbSign
                         );
    parcBuffer_Release(&bbSign);
    return signature;
}
static void *
_ccnxManifestHashGroupIterator_Init(CCNxManifestHashGroup *group)
{
    _HashgroupIteratorState *state = parcMemory_Allocate(sizeof(_HashgroupIteratorState));
    state->pointerNumber = 0;
    state->atEnd = false;
    return state;
}
Пример #6
0
void *
valueNewInt(int value)
{
    int *newValue = parcMemory_Allocate(sizeof(int));
    assertNotNull(newValue, "parcMemory_Allocate(%zu) returned NULL", sizeof(int));
    *newValue = value;
    return newValue;
}
Пример #7
0
void *
keyNewInt(int key)
{
    int *newKey = parcMemory_Allocate(sizeof(int));
    assertNotNull(newKey, "parcMemory_Allocate(%zu) returned NULL", sizeof(int));
    *newKey = key;
    return newKey;
}
/*
 * Small size allocator for creating a network buffer
 */
static size_t
_allocator(void *userarg, size_t bytes, void **output)
{
    const size_t allocationSize = *(size_t *) userarg;
    void *allocated = parcMemory_Allocate(allocationSize);
    *output = allocated;
    return allocationSize;
}
Пример #9
0
static void *
_InitForward(_DummyChunker *chunker)
{
    _DummyChunkerState *state = parcMemory_Allocate(sizeof(_DummyChunkerState));

    state->val = 0;
    state->dir = 1;
    state->atEnd = false;

    return state;
}
Пример #10
0
static inline struct parc_deque_node *
_parcDequeNode_Create(void *element, struct parc_deque_node *previous, struct parc_deque_node *next)
{
    struct parc_deque_node *result = parcMemory_Allocate(sizeof(struct parc_deque_node));
    if (result != NULL) {
        result->element = element;
        result->next = next;
        result->previous = previous;
    }

    return result;
}
Пример #11
0
char *
parcBuffer_ToString(const PARCBuffer *buffer)
{
    size_t remaining = parcBuffer_Remaining(buffer);

    char *result = parcMemory_Allocate(remaining + 1);
    if (remaining > 0) {
        assertNotNull(result, "parcMemory_Allocate returned NULL");
        if (result != NULL) {
            memcpy(result, parcBuffer_Overlay((PARCBuffer *) buffer, 0), remaining);
        }
    }
    result[remaining] = 0;
    return result;
}
Пример #12
0
MetisTlvName *
metisTlvName_Create(const uint8_t *memory, size_t memoryLength)
{
    MetisTlvName *name = parcMemory_AllocateAndClear(sizeof(MetisTlvName));
    assertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisTlvName));

    name->memory = parcMemory_Allocate(memoryLength);
    assertNotNull(name->memory, "parcMemory_Allocate(%zu) returned NULL", memoryLength);

    memcpy(name->memory, memory, memoryLength);
    name->memoryLength = memoryLength;

    _setup(name);

    return name;
}
Пример #13
0
MetisTlvName *
metisTlvName_CreateFromCCNxName(const CCNxName *ccnxName)
{
    // to avoid reallocs, calculate the exact size we need
    size_t memoryLength = 0;
    for (size_t i = 0; i < ccnxName_GetSegmentCount(ccnxName); i++) {
        CCNxNameSegment *segment = ccnxName_GetSegment(ccnxName, i);
        memoryLength += 4 + ccnxNameSegment_Length(segment);
    }

    MetisTlvName *name = parcMemory_AllocateAndClear(sizeof(MetisTlvName));
    assertNotNull(name, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisTlvName));

    name->memoryLength = memoryLength;
    name->memory = parcMemory_Allocate(memoryLength);
    assertNotNull(name->memory, "parcMemory_Allocate(%zu) returned NULL", memoryLength);

    uint8_t *p = name->memory;
    uint8_t *end = p + memoryLength;
    for (size_t i = 0; i < ccnxName_GetSegmentCount(ccnxName); i++) {
        CCNxNameSegment *segment = ccnxName_GetSegment(ccnxName, i);
        uint16_t type = ccnxNameSegment_GetType(segment);
        uint16_t length = ccnxNameSegment_Length(segment);

        *(uint16_t *) p = htons(type);
        p += 2;

        *(uint16_t *) p = htons(length);
        p += 2;

        if (length >0) {
            PARCBuffer *buffer = ccnxNameSegment_GetValue(segment);
            uint8_t *overlay = parcBuffer_Overlay(buffer, 0);
            memcpy(p, overlay, length);

            p += length;
        }

        // sanity check
        assertTrue(p <= end, "Wrote past the end of buffer, now at %p end at %p", p, end);
    }

    _setup(name);
    return name;
}
Пример #14
0
static void *
_InitForward(PARCBufferChunker *chunker)
{
    _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState));

    state->chunkNumber = 0;
    state->direction = 0;
    state->position = 0;
    state->atEnd = false;

    if (parcBuffer_Remaining(chunker->data) < chunker->chunkSize) {
        state->nextChunkSize = parcBuffer_Remaining(chunker->data);
    } else {
        state->nextChunkSize = chunker->chunkSize;
    }

    return state;
}
Пример #15
0
static void *
_hmacCreate(void *env)
{
    _PARCAesSignerFileStore *keystore = (_PARCAesSignerFileStore *) env;

    // HMAC_Init_ex seems to overrun the size of HMAC_CTX, so make it bigger

    HMAC_CTX *ctx = parcMemory_Allocate(sizeof(HMAC_CTX) * 2);
    assertNotNull(ctx, "parcMemory_Allocate(%zu) returned NULL for HMAC_CTX", sizeof(HMAC_CTX) * 2);

    HMAC_CTX_init(ctx);

    // Now initialize it with our digest and key, so in hmac_init we can avoid using those
    assertTrue(parcBuffer_Remaining(keystore->secretKey) < INT_MAX, "The keystore secret key cannot be longer than %d", INT_MAX);
    HMAC_Init_ex(ctx, parcByteArray_Array(parcBuffer_Array(keystore->secretKey)), (int) parcBuffer_Remaining(keystore->secretKey), keystore->opensslMd, NULL);

    return ctx;
}
Пример #16
0
MetisHopByHopFragmenter *
metisHopByHopFragmenter_Create(MetisLogger *logger, unsigned mtu)
{
    MetisHopByHopFragmenter *fragmenter = parcMemory_Allocate(sizeof(MetisHopByHopFragmenter));
    if (fragmenter) {
        fragmenter->nextReceiveFragSequenceNumber = 0;
        fragmenter->nextSendFragSequenceNumber = 0;

        fragmenter->logger = metisLogger_Acquire(logger);
        fragmenter->mtu = mtu;
        fragmenter->receiveQueueCapacity = 128;  // this is a many-to-one queue, so not too big
        fragmenter->sendQueueCapacity = 2048;    // this is a one-to-many queue, so bigger (e.g. 64 KB in to 1KB payloads)
        fragmenter->receiveQueue = parcRingBuffer1x1_Create(fragmenter->receiveQueueCapacity, _ringBufferDestroyer);
        fragmenter->sendQueue = parcRingBuffer1x1_Create(fragmenter->sendQueueCapacity, _ringBufferDestroyer);
        fragmenter->currentReceiveBuffer = parcEventBuffer_Create();
        fragmenter->parserState = _ParserState_Idle;
    }
    return fragmenter;
}
Пример #17
0
static void *
_hmacCreate(void *env)
{
    PARCSymmetricKeySigner *signer = (PARCSymmetricKeySigner *) env;

    // HMAC_Init_ex seems to overrun the size of HMAC_CTX, so make it bigger

    HMAC_CTX *ctx = parcMemory_Allocate(sizeof(HMAC_CTX) * 2);
    assertNotNull(ctx, "parcMemory_Allocate(%zu) returned NULL for HMAC_CTX", sizeof(HMAC_CTX) * 2);

    HMAC_CTX_init(ctx);

    // Now initialize it with our digest and key, so in hmac_init we can avoid using those
    PARCBuffer *secretKey = parcSymmetricKeyStore_GetKey(signer->keyStore);
    assertTrue(parcBuffer_Remaining(secretKey) < 512, "The keystore secret key cannot be longer than %d", 512);

    HMAC_Init_ex(ctx, parcByteArray_Array(parcBuffer_Array(secretKey)), (int) parcBuffer_Remaining(secretKey), signer->opensslMd, NULL);

    return ctx;
}
Пример #18
0
static void *
_InitReverse(PARCFileChunker *chunker)
{
    _ChunkerState *state = parcMemory_Allocate(sizeof(_ChunkerState));

    state->chunkNumber = 0;
    state->direction = 1;
    state->atEnd = false;
    state->totalSize = parcFile_GetFileSize(chunker->file);

    if (state->totalSize < chunker->chunkSize) {
        state->position = 0;
        state->nextChunkSize = state->totalSize;
    } else {
        state->position = state->totalSize - chunker->chunkSize;
        state->nextChunkSize = chunker->chunkSize;
    }

    return state;
}
LONGBOW_TEST_CASE(Global, ccnxWireFormatMessage_PutGetIoVec)
{
    uint8_t *data = parcMemory_Allocate(64);
    memset(data, 0, 64);

    PARCBuffer *buffer = parcBuffer_Allocate(1);
    CCNxCodecNetworkBuffer *netbuff = ccnxCodecNetworkBuffer_CreateFromArray(&ParcMemoryMemoryBlock, NULL, 64, data);
    CCNxCodecNetworkBufferIoVec *iovec = ccnxCodecNetworkBuffer_CreateIoVec(netbuff);

    CCNxTlvDictionary *packet = ccnxCodecSchemaV1TlvDictionary_CreateInterest();
    ccnxWireFormatMessage_PutIoVec((CCNxWireFormatMessage *) packet, iovec);

    CCNxCodecNetworkBufferIoVec *test = ccnxWireFormatMessage_GetIoVec((CCNxWireFormatMessage *) packet);
    assertTrue(test == iovec, "Failed to get iovec from dictionary, expected %p got %p", (void *) iovec, (void *) test);

    ccnxTlvDictionary_Release(&packet);
    parcBuffer_Release(&buffer);
    ccnxCodecNetworkBufferIoVec_Release(&iovec);
    ccnxCodecNetworkBuffer_Release(&netbuff);
}
Пример #20
0
PARCEventSignal *
parcEventSignal_Create(PARCEventScheduler *eventScheduler, int signal, PARCEventType flags, PARCEvent_Callback *callback, void *callbackArgs)
{
    PARCEventSignal *parcEventSignal = parcMemory_Allocate(sizeof(PARCEventSignal));
    assertNotNull(parcEventSignal, "parcMemory_Allocate(%zu) returned NULL", sizeof(PARCEventSignal));

    parcEventSignal->eventScheduler = eventScheduler;
    parcEventSignal->callback = callback;
    parcEventSignal->callbackUserData = callbackArgs;

    parcEventSignal->event = event_new(parcEventScheduler_GetEvBase(eventScheduler), signal,
                                       internal_PARCEventType_to_libevent_type(flags),
                                       _parc_event_signal_callback, parcEventSignal);
    assertNotNull(parcEventSignal->event, "Could not create a new event!");

    parcEventSignal_LogDebug(parcEventSignal,
                             "parcEventSignal_Create(base=%p,signal=%x,flags=%x,cb=%p,args=%p) = %p\n",
                             parcEventScheduler_GetEvBase(eventScheduler), signal, flags,
                             callback, callbackArgs, parcEventSignal);
    return parcEventSignal;
}
Пример #21
0
char *
parcMemory_Format(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);

    va_list copy;
    va_copy(copy, ap);
    int length = vsnprintf(NULL, 0, format, copy);
    va_end(copy);

    char *result = NULL;
    if (length >= 0) {
        result = parcMemory_Allocate(length + 1);

        if (result != NULL) {
            vsprintf(result, format, ap);
        }
    }
    return result;
}
Пример #22
0
/**
 * Creates a client-specific session
 *
 *   <#Discussion#>
 *
 * @param <#param1#>
 * @return <#return#>
 *
 * Example:
 * @code
 * <#example#>
 * @endcode
 */
static _MetisCommandLineInterface_Session *
metisCliSession_Create(MetisCommandLineInterface *cli, MetisSocketType clientSocket, struct sockaddr *clientAddress, int clientAddressLength)
{
    _MetisCommandLineInterface_Session *session = parcMemory_AllocateAndClear(sizeof(_MetisCommandLineInterface_Session));
    assertNotNull(session, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisCommandLineInterface_Session));
    session->parentCli = cli;
    session->clientAddress = parcMemory_Allocate(clientAddressLength);
    assertNotNull(session->clientAddress, "parcMemory_Allocate(%d) returned NULL", clientAddressLength);
    session->clientAddressLength = clientAddressLength;
    session->clientSocket = clientSocket;

    memcpy(session->clientAddress, clientAddress, clientAddressLength);

    MetisDispatcher *dispatcher = metisForwarder_GetDispatcher(cli->metis);
    PARCEventScheduler *eventBase = metisDispatcher_GetEventScheduler(dispatcher);
    session->streamBuffer = parcEventQueue_Create(eventBase, clientSocket, PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks);

    parcEventQueue_SetCallbacks(session->streamBuffer, _metisCliSession_ReadCallback, NULL, _metisCliSession_EventCallback, session);
    parcEventQueue_Enable(session->streamBuffer, PARCEventType_Read);

    return session;
}
Пример #23
0
PARCHashMap *
parcHashMap_Copy(const PARCHashMap *original)
{
    parcHashMap_OptionalAssertValid(original);

    PARCHashMap *result = parcObject_CreateInstance(PARCHashMap);

    result->capacity = original->capacity;
    result->minCapacity = original->minCapacity;
    result->maxLoadFactor = original->maxLoadFactor;
    result->minLoadFactor = original->minLoadFactor;
    result->size = original->size;
    result->buckets = parcMemory_Allocate(result->capacity * sizeof(PARCLinkedList*));

    for (unsigned int i = 0; i < result->capacity; i++) {
        result->buckets[i] = NULL;
        if (original->buckets[i] != NULL) {
            result->buckets[i] = parcLinkedList_Copy(original->buckets[i]);
        }
    }

    return result;
}
LONGBOW_TEST_CASE(CreateRelease, CreateRelease)
{
    CCNxPortalFactory *factory = (CCNxPortalFactory *) longBowTestCase_GetClipBoardData(testCase);

    CCNxPortalAttributes *attributes = NULL;
    CCNxPortalStack *actual =
        ccnxPortalStack_Create(factory,
                               attributes,
                               _mockStart,
                               _mockStop,
                               _mockRead,
                               _mockSend,
                               _mockListen,
                               _mockIgnore,
                               _mockGetFileId,
                               _mockSetAttributes,
                               _mockGetAttributes,
                               parcMemory_Allocate(10),
                               parcMemory_DeallocateImpl);

    parcObjectTesting_AssertAcquire(actual);

    ccnxPortalStack_Release(&actual);
}
Пример #25
0
        return 1;
    }
}

static uint32_t
_dummy_HashCode(const _DummyObject *obj)
{
    _DummyObject *dummy = (_DummyObject *) obj;
    dummy->calledCount++;
    return 1337;
}

static char *
_dummy_ToString(const _DummyObject *x __attribute__((unused)))
{
    char *str = (char *) parcMemory_Allocate(6);
    char *test = "dummy";
    sprintf(str, "%s", test);
    return str;
}

static PARCJSON *
_dummy_ToJSON(const _DummyObject *x __attribute__((unused)))
{
    PARCJSON *json = parcJSON_ParseString("{ \"type\" : \"dummy\"  }");
    return json;
}

parcObject_ExtendPARCObject(_DummyObject, _dummy_Destroy, _dummy_Copy, _dummy_ToString,
                            _dummy_Equals, _dummy_Compare, _dummy_HashCode, _dummy_ToJSON);
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;
}