Example #1
0
static bool
_symbolicRegisterPrefix(MetisConfiguration *config, CPIRouteEntry *route)
{
    bool success = false;

    const char *symbolic = cpiRouteEntry_GetSymbolicName(route);
    unsigned ifidx = metisSymbolicNameTable_Get(config->symbolicNameTable, symbolic);
    if (ifidx != UINT32_MAX) {
        cpiRouteEntry_SetInterfaceIndex(route, ifidx);
        if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug)) {
            metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug, __func__,
                            "Add route resolve name '%s' to connid %u",
                            symbolic, ifidx);
        }

        success = metisForwarder_AddOrUpdateRoute(config->metis, route);
    } else {
        if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning)) {
            metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning, __func__,
                            "Add route symbolic name '%s' could not be resolved", symbolic);
        }
        // this is a failure
    }
    return success;
}
Example #2
0
/**
 * Reads from the socket to fill in the work buffer
 *
 * Reads one or more packets from the socket to the work buffer  It will append to the work buffer.
 * The BFP socket is non-blocking.  The BPF interface may return multiple packets in one read
 * that need to be parsed as in _darwinEthernet_ReadWorkBuffer().
 *
 * @param [in] ether The Darwin ethernet interface
 *
 * @retval true We added something to the work buffer
 * @retval false Nothing was read
 *
 * Example:
 * @code
 * <#example#>
 * @endcode
 */
static bool
_darwinEthernet_ReadSocket(MetisGenericEther *ether)
{
    bool success = false;

    if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
        metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                        "%s reading fd %d bufferLength %u", __func__, ether->etherSocket, ether->etherBufferLength);
    }

    // The buffer we're reading must be exactly ether->etherBufferLength
    // TODO: Fix the parcEventBuffer_ReadFromFileDescriptor call to reserve that space so it's all there.

    uint8_t tempBuffer[ether->etherBufferLength];
    ssize_t read_length = read(ether->etherSocket, tempBuffer, ether->etherBufferLength);
    if (read_length > 0) {
        parcEventBuffer_Append(ether->workBuffer, tempBuffer, read_length);
        if (read_length > 0) {
            if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
                metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                                "%s read %zd bytes from fd %d",
                                __func__,
                                read_length,
                                ether->etherSocket);
            }
            success = true;
        }
    }

    return success;
}
Example #3
0
static bool
_metisConfiguration_AddConnectionEthernet(MetisConfiguration *config, CPIConnectionEthernet *etherConn, CPIAddress *linkAddress, MetisListenerOps *listenerOps)
{
    bool success = false;

    const char *symbolic = cpiConnectionEthernet_GetSymbolicName(etherConn);
    if (!metisSymbolicNameTable_Exists(config->symbolicNameTable, symbolic)) {
        const CPIAddress *remote = cpiConnectionEthernet_GetPeerLinkAddress(etherConn);
        MetisAddressPair *pair = metisAddressPair_Create(linkAddress, remote);

        MetisGenericEther *ether = metisEtherListener_GetGenericEtherFromListener(listenerOps);

        if (ether) {
            MetisIoOperations *ops = metisEtherConnection_Create(config->metis, ether, pair);

            if (ops) {
                MetisConnection *conn = metisConnection_Create(ops);
                assertNotNull(conn, "Failed to create connection");

                metisConnectionTable_Add(metisForwarder_GetConnectionTable(config->metis), conn);
                metisSymbolicNameTable_Add(config->symbolicNameTable, symbolic, metisConnection_GetConnectionId(conn));

                success = true;

                if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug)) {
                    char *peerAddressString = cpiAddress_ToString(remote);
                    metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug, __func__,
                                    "Add connection %s on %s to %s, connid %u",
                                    symbolic,
                                    cpiConnectionEthernet_GetInterfaceName(etherConn),
                                    peerAddressString,
                                    metisConnection_GetConnectionId(conn));
                    parcMemory_Deallocate((void **) &peerAddressString);
                }
            }
        } else {
            metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__,
                            "Could not get MetisGenericEther for listener %p",
                            listenerOps);
        }

        metisAddressPair_Release(&pair);
    } else {
        if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning)) {
            const CPIAddress *remote = cpiConnectionEthernet_GetPeerLinkAddress(etherConn);
            char *peerAddressString = cpiAddress_ToString(remote);
            metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Warning, __func__,
                            "Add connection %s on %s to %s failed, symbolic name exists",
                            symbolic,
                            cpiConnectionEthernet_GetInterfaceName(etherConn),
                            peerAddressString);
            parcMemory_Deallocate((void **) &peerAddressString);
        }
    }

    return success;
}
Example #4
0
static void
_conn_eventcb(PARCEventQueue *event, PARCEventQueueEventType events, void *ioOpsVoid)
{
    MetisIoOperations *ops = (MetisIoOperations *) ioOpsVoid;
    _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);

    if (events & PARCEventQueueEventType_Connected) {
        if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) {
            metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__,
                            "Connection %u is connected", stream->id);
        }

        // if the stream was closed, do not transition to an UP state
        if (!stream->isClosed) {
            _setConnectionState(stream, true);
        }
    } else
    if (events & PARCEventQueueEventType_EOF) {
        if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) {
            metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__,
                            "connid %u closed.",
                            stream->id);
        }

        parcEventQueue_Disable(stream->bufferEventVector, PARCEventType_Read);

        _setConnectionState(stream, false);

        if (!stream->isClosed) {
            stream->isClosed = true;
            // this will cause the connection manager to destroy the connection later
            metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionClosed, stream->id));
        }
    } else
    if (events & PARCEventQueueEventType_Error) {
        if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Error)) {
            metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__,
                            "Got an error on the connection %u: %s", stream->id, strerror(errno));
        }

        parcEventQueue_Disable(stream->bufferEventVector, PARCEventType_Read | PARCEventType_Write);

        _setConnectionState(stream, false);

        if (!stream->isClosed) {
            stream->isClosed = true;
            // this will cause the connection manager to destroy the connection later
            metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionClosed, stream->id));
        }
    }
    /* None of the other events can happen here, since we haven't enabled
     * timeouts */
}
Example #5
0
// There's a bit too much going on in this function, need to break it
// apart for testability and style.
static MetisPITVerdict
_metisStandardPIT_ReceiveInterest(MetisPIT *generic, MetisMessage *interestMessage)
{
    assertNotNull(generic, "Parameter pit must be non-null");
    assertNotNull(interestMessage, "Parameter interestMessage must be non-null");

    MetisStandardPIT *pit = metisPIT_Closure(generic);

    MetisPitEntry *pitEntry = metisMatchingRulesTable_Get(pit->table, interestMessage);

    if (pitEntry) {
        // has it expired?
        MetisTicks now = metisForwarder_GetTicks(pit->metis);
        if (now < metisPitEntry_GetExpiryTime(pitEntry)) {
            // what should we do about extending the lifetime? (case 819)

            _metisPIT_ExtendLifetime(pit, pitEntry, interestMessage);

            // Is the reverse path already in the PIT entry?
            if (_metisPIT_IngressSetContains(pitEntry, metisMessage_GetIngressConnectionId(interestMessage))) {
                // It is already in the PIT entry, so this is a retransmission, so forward it.

                if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
                    metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                                    "Message %p existing entry (expiry %" PRIu64 ") and reverse path, forwarding",
                                    (void *) interestMessage,
                                    metisPitEntry_GetExpiryTime(pitEntry));
                }

                return MetisPITVerdict_Forward;
            }

            // It is in the PIT but this is the first interest for the reverse path
            metisPitEntry_AddIngressId(pitEntry, metisMessage_GetIngressConnectionId(interestMessage));

            if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
                metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                                "Message %p existing entry (expiry %" PRIu64 ") and reverse path is new, aggregate",
                                (void *) interestMessage,
                                metisPitEntry_GetExpiryTime(pitEntry));
            }

            return MetisPITVerdict_Aggregate;
        }

        // it's an old entry, remove it
        metisMatchingRulesTable_RemoveFromBest(pit->table, interestMessage);
    }

    _metisPIT_StoreInTable(pit, interestMessage);

    return MetisPITVerdict_Forward;
}
Example #6
0
MetisGenericEther *
metisGenericEther_Create(MetisForwarder *metis, const char *deviceName, uint16_t etherType)
{
    assertNotNull(metis, "Parameter metis must be non-null");

    // The Darwin generic ether allows a NULL device name, it is used in the unit tests.

    MetisGenericEther *ether = NULL;

    if (metisEthernet_IsValidEthertype(etherType)) {
        ether = parcObject_CreateInstance(MetisGenericEther);
        ether->ethertype = etherType;
        ether->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
        ether->etherSocket = -1; // invalid valid
        ether->workBuffer = parcEventBuffer_Create();
        ether->macAddress = NULL;
        ether->mtu = metisSystem_InterfaceMtu(metis, deviceName);

        _darwinEthernet_SetInterfaceAddress(ether, deviceName);

        bool success = _darwinEthernet_SetupReceive(ether, deviceName);

        if (success) {
            if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) {
                char *str = parcBuffer_ToHexString(ether->macAddress);
                metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__,
                                "GenericEther %p created on device %s (%s) for ethertype 0x%04x fd %d bufferLength %u mtu %u",
                                (void *) ether, deviceName, str, etherType, ether->etherSocket, ether->etherBufferLength, ether->mtu);
                parcMemory_Deallocate((void **) &str);
            }
        } else {
            if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Error)) {
                metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__,
                                "GenericEther failed to created on device %s for ethertype 0x%04x",
                                deviceName, etherType);
            }

            // this will also null ether
            metisGenericEther_Release(&ether);
        }

        assertTrue(ether->etherBufferLength < 65536, "Buffer length way too big, expected less than 65536 got %u", ether->etherBufferLength);
    } else {
        if (metisLogger_IsLoggable(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Error)) {
            metisLogger_Log(metisForwarder_GetLogger(metis), MetisLoggerFacility_IO, PARCLogLevel_Error, __func__,
                            "GenericEther failed to created on device %s for ethertype 0x%04x, invalid ethertype",
                            deviceName, etherType);
        }
    }

    return ether;
}
Example #7
0
static MetisMessage *
_metisLRUContentStore_MatchInterest(MetisContentStoreInterface *storeImpl, MetisMessage *interest)
{
    MetisMessage *result = NULL;

    _MetisLRUContentStore *store = (_MetisLRUContentStore *) metisContentStoreInterface_GetPrivateData(storeImpl);

    assertNotNull(store, "Parameter store must be non-null");
    assertNotNull(interest, "Parameter interestMessage must be non-null");
    assertTrue(metisMessage_GetType(interest) == MetisMessagePacketType_Interest,
               "Parameter interestMessage must be an Interest");

    // This will do the most restrictive lookup.
    // a) If the interest has a ContentObjectHash restriction, it will look only in the ByNameAndObjectHash table.
    // b) If it has a KeyId, it will look only in the ByNameAndKeyId table.
    // c) otherwise, it looks only in the ByName table.

    PARCHashCodeTable *table;
    if (metisMessage_HasContentObjectHash(interest)) {
        table = store->storageByNameAndObjectHashHash;
    } else if (metisMessage_HasKeyId(interest)) {
        table = store->indexByNameAndKeyIdHash;
    } else {
        table = store->indexByNameHash;
    }

    MetisContentStoreEntry *storeEntry = parcHashCodeTable_Get(table, interest);

    if (storeEntry) {
        metisContentStoreEntry_MoveToHead(storeEntry);
        result = metisContentStoreEntry_GetMessage(storeEntry);

        store->stats.countHits++;

        if (metisLogger_IsLoggable(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
            metisLogger_Log(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                            "LRUContentStore %p matched interest %p (hits %" PRIu64 ", misses %" PRIu64 ")",
                            (void *) store, (void *) interest, store->stats.countHits, store->stats.countMisses);
        }
    } else {
        store->stats.countMisses++;

        if (metisLogger_IsLoggable(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
            metisLogger_Log(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                            "LRUContentStore %p missed interest %p (hits %" PRIu64 ", misses %" PRIu64 ")",
                            (void *) store, (void *) interest, store->stats.countHits, store->stats.countMisses);
        }
    }

    return result;
}
Example #8
0
MetisPIT *
metisStandardPIT_Create(MetisForwarder *metis)
{
    assertNotNull(metis, "Parameter must be non-null");

    size_t allocation = sizeof(MetisPIT) + sizeof(MetisStandardPIT);

    MetisPIT *generic = parcMemory_AllocateAndClear(allocation);
    assertNotNull(generic, "parcMemory_AllocateAndClear(%zu) returned NULL", allocation);
    generic->closure = (uint8_t *) generic + sizeof(MetisPIT);

    MetisStandardPIT *pit = metisPIT_Closure(generic);
    pit->metis = metis;
    pit->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
    pit->table = metisMatchingRulesTable_Create(_metisPIT_PitEntryDestroyer);

    if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
        metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                        "PIT %p created",
                        (void *) pit);
    }

    generic->getPitEntry = _metisStandardPIT_GetPitEntry;
    generic->receiveInterest = _metisStandardPIT_ReceiveInterest;
    generic->release = _metisStandardPIT_Destroy;
    generic->removeInterest = _metisStandardPIT_RemoveInterest;
    generic->satisfyInterest = _metisStandardPIT_SatisfyInterest;

    return generic;
}
Example #9
0
static void
_metisStreamConnection_DestroyOperations(MetisIoOperations **opsPtr)
{
    assertNotNull(opsPtr, "Parameter opsPtr must be non-null double pointer");
    assertNotNull(*opsPtr, "Parameter opsPtr must dereference to non-null pointer");

    MetisIoOperations *ops = *opsPtr;
    assertNotNull(metisIoOperations_GetClosure(ops), "ops->context must not be null");

    _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);

    parcEventQueue_Destroy(&stream->bufferEventVector);

    metisAddressPair_Release(&stream->addressPair);

    if (!stream->isClosed) {
        stream->isClosed = true;
        metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionClosed, stream->id));
    }

    metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionDestroyed, stream->id));

    if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) {
        metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__,
                        "StreamConnection %p destroyed", (void *) stream);
    }

    metisLogger_Release(&stream->logger);
    parcMemory_Deallocate((void **) &stream);
    parcMemory_Deallocate((void **) &ops);

    *opsPtr = NULL;
}
Example #10
0
void
metisConfiguration_Receive(MetisConfiguration *config, MetisMessage *message)
{
    assertNotNull(config, "Parameter config must be non-null");
    assertNotNull(message, "Parameter message must be non-null");
    assertTrue(metisMessage_GetType(message) == MetisMessagePacketType_Control,
               "Message must be type CPI, expected %02x got %02x",
               MetisMessagePacketType_Control, metisMessage_GetType(message));

    CCNxControl *control = metisMessage_CreateControlMessage(message);
    unsigned ingressId = metisMessage_GetIngressConnectionId(message);

    if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug)) {
        char *str = parcJSON_ToCompactString(ccnxControl_GetJson(control));
        metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Debug, __func__,
                        "%s received %s\n", __func__, str);
        parcMemory_Deallocate((void **) &str);
    }

    CCNxControl *response = _processControl(config, control, ingressId);
    metisConfiguration_SendResponse(config, response, ingressId);
    ccnxControl_Release(&response);

    ccnxControl_Release(&control);
    metisMessage_Release(&message);
}
Example #11
0
bool
metisTlvSkeleton_Parse(MetisTlvSkeleton *opaque, uint8_t *packet, MetisLogger *logger)
{
    // do not assert invariants here.  Parse will setup the invariants.
    _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
    uint8_t version = packet[0];

    switch (version) {
        case 0:
            _initialize(skeleton, &MetisTlvSchemaV0_Ops, packet, logger);
            return MetisTlvSchemaV0_Ops.parse(opaque);

        case 1:
            _initialize(skeleton, &MetisTlvSchemaV1_Ops, packet, logger);
            return MetisTlvSchemaV1_Ops.parse(opaque);

        default:
            if (metisLogger_IsLoggable(logger, MetisLoggerFacility_Message, PARCLogLevel_Warning)) {
                metisLogger_Log(logger, MetisLoggerFacility_Message, PARCLogLevel_Warning, __func__,
                                "Parsing unknown packet version %u", version);
            }
            break;
    }
    return false;
}
Example #12
0
bool
metisHopByHopFragmenter_Send(MetisHopByHopFragmenter *fragmenter, MetisMessage *message)
{
    bool success = false;

    // If the packet will fit in the MTU without fragmentation, do not use fragmentation
    if (metisMessage_Length(message) > fragmenter->mtu) {
        success = _sendFragments(fragmenter, message);
    } else {
        MetisMessage *copy = metisMessage_Acquire(message);
        success = parcRingBuffer1x1_Put(fragmenter->sendQueue, copy);
        if (!success) {
            metisMessage_Release(&copy);
            metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
                            "Failed to add message %p to send queue", (void *) message);
        } else {
            if (metisLogger_IsLoggable(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
                metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                                "Add message %p to send queue", (void *) message);
            }
        }
    }

    return success;
}
Example #13
0
/**
 * @function metisStreamConnection_Send
 * @abstract Non-destructive send of the message.
 * @discussion
 *   Send uses metisMessage_CopyToStreamBuffer, which is a non-destructive write.
 *   The send may fail if there's no buffer space in the output queue.
 *
 * @param dummy is ignored.  A stream has only one peer.
 * @return <#return#>
 */
static bool
_metisStreamConnection_Send(MetisIoOperations *ops, const CPIAddress *dummy, MetisMessage *message)
{
    assertNotNull(ops, "Parameter ops must be non-null");
    assertNotNull(message, "Parameter message must be non-null");
    _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);

    bool success = false;
    if (stream->isUp) {
        PARCEventBuffer *buffer = parcEventBuffer_GetQueueBufferOutput(stream->bufferEventVector);
        size_t buffer_backlog = parcEventBuffer_GetLength(buffer);
        parcEventBuffer_Destroy(&buffer);

        if (buffer_backlog < OUTPUT_QUEUE_BYTES) {
            if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
                metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                                "connid %u Writing %zu bytes to buffer with backlog %zu bytes",
                                stream->id,
                                metisMessage_Length(message),
                                buffer_backlog);
            }

            int failure = metisMessage_Write(stream->bufferEventVector, message);
            if (failure == 0) {
                success = true;
            }
        } else {
            if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning)) {
                metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
                                "connid %u Writing to buffer backlog %zu bytes DROP MESSAGE",
                                stream->id,
                                buffer_backlog);
            }
        }
    } else {
        if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Error)) {
            metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__,
                            "connid %u tried to send to down connection (isUp %d isClosed %d)",
                            stream->id,
                            stream->isUp,
                            stream->isClosed);
        }
    }

    return success;
}
Example #14
0
static void
_evictByStorePolicy(_MetisLRUContentStore *store, uint64_t currentTimeInMetisTicks)
{
    // We need to make room. Here's the plan:
    //  1) Check to see if anything has expired. If so, remove it and we're done. If not,
    //  2) Check to see if anything has exceeded it's recommended cache time. If so, remove it and we're done. If not,
    //  3) Remove the least recently used item.

    MetisContentStoreEntry *entry = metisTimeOrderedList_GetOldest(store->indexByExpirationTime);
    if (entry
        && metisContentStoreEntry_HasExpiryTimeTicks(entry)
        && (currentTimeInMetisTicks > metisContentStoreEntry_GetExpiryTimeTicks(entry))) {
        // Found an expired entry. Remove it, and we're done.

        store->stats.countExpiryEvictions++;
        if (metisLogger_IsLoggable(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
            metisLogger_Log(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                            "ContentStore %p evict message %p by ExpiryTime (ExpiryTime evictions %" PRIu64 ")",
                            (void *) store, (void *) metisContentStoreEntry_GetMessage(entry),
                            store->stats.countExpiryEvictions);
        }

        _metisLRUContentStore_PurgeStoreEntry(store, entry);
    } else {
        // Check for entries that have exceeded RCT
        entry = metisTimeOrderedList_GetOldest(store->indexByRecommendedCacheTime);
        if (entry
            && metisContentStoreEntry_HasRecommendedCacheTimeTicks(entry)
            && (currentTimeInMetisTicks > metisContentStoreEntry_GetRecommendedCacheTimeTicks(entry))) {
            // Found an entry passed it's RCT. Remove it, and we're done.

            store->stats.countRCTEvictions++;
            if (metisLogger_IsLoggable(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
                metisLogger_Log(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                                "ContentStore %p evict message %p by RCT (RCT evictions %" PRIu64 ")",
                                (void *) store, (void *) metisContentStoreEntry_GetMessage(entry),
                                store->stats.countRCTEvictions);
            }

            _metisLRUContentStore_PurgeStoreEntry(store, entry);
        } else {
            store->stats.countLruEvictions++;
            _metisLRUContentStore_RemoveLeastUsed(store);
        }
    }
}
Example #15
0
MetisConfigurationFile *
metisConfigurationFile_Create(MetisForwarder *metis, const char *filename)
{
    assertNotNull(metis, "Parameter metis must be non-null");
    assertNotNull(filename, "Parameter filename must be non-null");

    MetisConfigurationFile *configFile = parcObject_CreateInstance(MetisConfigurationFile);

    if (configFile) {
        configFile->linesRead = 0;
        configFile->metis = metis;
        configFile->filename = parcMemory_StringDuplicate(filename, strlen(filename));
        assertNotNull(configFile->filename, "Could not copy string '%s'", filename);

        // setup the control state for the command parser
        configFile->controlState = metisControlState_Create(configFile, _writeRead);

        // we do not register Help commands
        metisControlState_RegisterCommand(configFile->controlState, metisControlRoot_Create(configFile->controlState));

        // open the file and make sure we can read it
        configFile->fh = fopen(configFile->filename, "r");

        if (configFile->fh) {
            if (metisLogger_IsLoggable(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug)) {
                metisLogger_Log(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Debug, __func__,
                                "Open config file %s",
                                configFile->filename);
            }
        } else {
            if (metisLogger_IsLoggable(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Error)) {
                metisLogger_Log(metisForwarder_GetLogger(metis), MetisLoggerFacility_Config, PARCLogLevel_Error, __func__,
                                "Could not open config file %s: (%d) %s",
                                configFile->filename,
                                errno,
                                strerror(errno));
            }

            // failure cleanup the object -- this nulls it so final return null be NULL
            metisConfigurationFile_Release(&configFile);
        }
    }
    return configFile;
}
Example #16
0
/**
 * Parses the work buffer to extract packets
 *
 * The work buffer should be filled in with a set of tuples (bh_hdrlen, frame, pad).
 * The pad extends each packet out to BPF_WORDALIGN.
 *
 * If the CCNxMessage PacketLength says it is larger than the read capture length (caplen),
 * then this is an invalid packet and it will be discarded.  This error will result in a
 * ReadWorkBufferResult_TryAgain condition.
 *
 * struct bpf_hdr {
 *    struct BPF_TIMEVAL bh_tstamp;	 // time stamp
 *    bpf_u_int32        bh_caplen;	 // length of captured portion
 *    bpf_u_int32        bh_datalen; // original length of packet
 *    u_short		     bh_hdrlen;	 // length of bpf header (this struct plus alignment padding)
 * }
 *
 * @param [in] ether An allocated Darwin ethernet.
 * @param [in] readbuffer A user-provided read buffer.
 *
 * @retval ReadWorkBufferResult_Ok A frame was moved from workbuffer to readbuffer
 * @retval ReadWorkBufferResult_Empty There's not enough bytes in the workbuffer
 * @retval ReadWorkBufferResult_TryAgain (likely discard) caused this call to fail, but you should try again
 *
 * Example:
 * @code
 * <#example#>
 * @endcode
 */
static _ReadWorkBufferResult
_darwinEthernet_ReadWorkBuffer(MetisGenericEther *ether, PARCEventBuffer *readbuffer)
{
    _ReadWorkBufferResult result = ReadWorkBufferResult_Empty;

    // Make sure we have linear memory for the BPF header
    struct bpf_hdr *bpfHeader = (struct bpf_hdr *) parcEventBuffer_Pullup(ether->workBuffer, sizeof(struct bpf_hdr));

    // make sure we have enough bytes to process the frame
    // bpfHeader may be NULL if there are not sizeof(struct bpf_hdr) bytes available.
    if (bpfHeader && parcEventBuffer_GetLength(ether->workBuffer) >= bpfHeader->bh_hdrlen + bpfHeader->bh_caplen) {
        // (0) Save the needed fields from the bpf header
        // (1) pop off the bpf header
        // (2) move the iovec from work buffer to readBuffer.
        // (3) remove any BPF_WORDALIGN padding to the start of the next packet

        // (0) Save the needed fields from the bpf header
        uint16_t hdrlen = bpfHeader->bh_hdrlen;
        uint32_t caplen = bpfHeader->bh_caplen;

        // (1) pop off the bpf header
        parcEventBuffer_Read(ether->workBuffer, NULL, hdrlen);

        // (1a) Determine the packet length from the fixed header and only transfer that many bytes
        uint16_t packetlen = _getFrameLengthFromWorkBuffer(ether);

        if (packetlen <= caplen) {
            // (2) move the iovec from work buffer to readBuffer.
            parcEventBuffer_ReadIntoBuffer(ether->workBuffer, readbuffer, packetlen);

            // (2a) drain off any trailer (i.e. FCS)
            parcEventBuffer_Read(ether->workBuffer, NULL, caplen - packetlen);

            result = ReadWorkBufferResult_Ok;
        } else {
            if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning)) {
                metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__,
                                "%s reading fd %d discard packetlen %u greater than caplen %u",
                                __func__, ether->etherSocket, packetlen, caplen);
            }

            // discard all of caplen
            parcEventBuffer_Read(ether->workBuffer, NULL, caplen);

            // tell the caller that this read failed, but they could try again.
            result = ReadWorkBufferResult_TryAgain;
        }

        // (3) remove any BPF_WORDALIGN padding to the start of the next packet
        size_t alignedLength = BPF_WORDALIGN(hdrlen + caplen);
        size_t pad = alignedLength - hdrlen - caplen;
        parcEventBuffer_Read(ether->workBuffer, NULL, pad);
    }
    return result;
}
Example #17
0
void
metisTlvSkeleton_SetFragmentPayload(MetisTlvSkeleton *opaque, size_t offset, size_t length)
{
    _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
    skeleton->array[INDEX_FRAGMENTPAYLOAD].offset = offset;
    skeleton->array[INDEX_FRAGMENTPAYLOAD].length = length;

    if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
        metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
                        "Set fragment payload extent {%u, %u}", offset, length);
    }
}
Example #18
0
void
metisTlvSkeleton_SetCPI(MetisTlvSkeleton *opaque, size_t offset, size_t length)
{
    _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
    skeleton->array[INDEX_CPI].offset = offset;
    skeleton->array[INDEX_CPI].length = length;

    if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
        metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
                        "Set cpi extent {%u, %u}", offset, length);
    }
}
Example #19
0
/**
 * @function single_read
 * @abstract Read at most 1 message from the network
 * @discussion
 *   If a complete message is ready on the input buffer, will allocate and return it.
 *
 *   This function manipulates the stream->nextMessageLength.  (1) Initializes with nextMessageLength = 0,
 *   which means we have not started parsing a packet.  (2) After reading a FixedHeader, set nextMessageLength
 *   to the total length of the message.  (3) After reading nextMessageLength bytes, return the outputBuffer
 *   and reset nextMessageLength to 0.
 *
 * @param input is the PARCEventBuffer to read
 * @param stream is the related stream state for the input
 * @return true if there's more to read after this message.
 */
static MetisMessage *
_single_read(PARCEventBuffer *input, _MetisStreamState *stream)
{
    size_t bytesAvailable = parcEventBuffer_GetLength(input);

    assertTrue(bytesAvailable >= metisTlv_FixedHeaderLength(), "Called with too short an input: %zu", bytesAvailable);

    if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
        metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                        "connid %u read %zu bytes",
                        stream->id, bytesAvailable);
    }

    if (stream->nextMessageLength == 0) {
        _startNewMessage(stream, input, bytesAvailable);
    }

    // This is not an ELSE statement.  We can both start a new message then
    // check if there's enough bytes to read the whole thing.

    if (bytesAvailable >= stream->nextMessageLength) {
        MetisMessage *message = _readMessage(stream, metisForwarder_GetTicks(stream->metis), input);

        if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
            metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                            "connid %u msg_length %zu read_length %zu, resetting parser",
                            stream->id,
                            stream->nextMessageLength,
                            bytesAvailable);
        }

        // now reset message length for next packet
        stream->nextMessageLength = 0;

        return message;
    }

    return NULL;
}
Example #20
0
static void
_metisStandardPIT_RemoveInterest(MetisPIT *generic, const MetisMessage *interestMessage)
{
    assertNotNull(generic, "Parameter pit must be non-null");
    assertNotNull(interestMessage, "Parameter interestMessage must be non-null");

    MetisStandardPIT *pit = metisPIT_Closure(generic);

    if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
        metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                        "Message %p removed from PIT",
                        (void *) interestMessage);
    }

    metisMatchingRulesTable_RemoveFromBest(pit->table, interestMessage);
}
Example #21
0
MetisIoOperations *
metisStreamConnection_OpenConnection(MetisForwarder *metis, MetisAddressPair *pair, bool isLocal)
{
    assertNotNull(metis, "Parameter metis must be non-null");
    assertNotNull(pair, "Parameter pair must be non-null");

    // if there's an error on the bind or connect, will return NULL
    PARCEventQueue *bufferEventVector = metisDispatcher_StreamBufferConnect(metisForwarder_GetDispatcher(metis), pair);
    if (bufferEventVector == NULL) {
        // error opening connection
        return NULL;
    }

    _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
    assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));

    stream->metis = metis;
    stream->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
    stream->fd = parcEventQueue_GetFileDescriptor(bufferEventVector);
    stream->bufferEventVector = bufferEventVector;
    stream->id = metisForwarder_GetNextConnectionId(metis);
    stream->addressPair = pair;
    stream->isClosed = false;

    // allocate a connection
    MetisIoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(MetisIoOperations));
    assertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisIoOperations));
    memcpy(io_ops, &_template, sizeof(MetisIoOperations));
    io_ops->closure = stream;

    parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, _conn_eventcb, (void *) io_ops);
    parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read);

    // we start in DOWN state, until remote side answers
    metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionCreate, stream->id));
    _setConnectionState(stream, false);

    if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info)) {
        char *pair_str = metisAddressPair_ToString(pair);
        metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__,
                        "StreamConnection %p connect for address pair %s", (void *) stream, pair_str);
        free(pair_str);
    }

    return io_ops;
}
Example #22
0
static void
_metisStandardPIT_Destroy(MetisPIT **pitPtr)
{
    assertNotNull(pitPtr, "Parameter must be non-null double pointer");
    assertNotNull(*pitPtr, "Parameter must dereference to non-null pointer");

    MetisStandardPIT *pit = metisPIT_Closure(*pitPtr);

    if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
        metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                        "PIT %p destroyed",
                        (void *) pit);
    }

    metisMatchingRulesTable_Destroy(&pit->table);
    metisLogger_Release(&pit->logger);
    parcMemory_Deallocate(pitPtr);
}
Example #23
0
bool
metisTlvSkeleton_UpdateHopLimit(MetisTlvSkeleton *opaque, uint8_t hoplimit)
{
    _InternalSkeleton *skeleton = (_InternalSkeleton *) opaque;
    bool updated = false;
    if (!metisTlvExtent_Equals(&skeleton->array[INDEX_HOPLIMIT], &metisTlvExtent_NotFound)) {
        if (skeleton->array[INDEX_HOPLIMIT].length == 1) {
            updated = true;
            uint8_t *value = skeleton->packet + skeleton->array[INDEX_HOPLIMIT].offset;
            *value = hoplimit;

            if (metisLogger_IsLoggable(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug)) {
                metisLogger_Log(skeleton->logger, MetisLoggerFacility_Message, PARCLogLevel_Debug, __func__,
                                "set hoplimit %u", hoplimit);
            }
        }
    }
    return updated;
}
Example #24
0
MetisContentStoreInterface *
metisLRUContentStore_Create(MetisContentStoreConfig *config, MetisLogger *logger)
{
    MetisContentStoreInterface *storeImpl = NULL;

    assertNotNull(logger, "MetisLRUContentStore requires a non-NULL logger");

    storeImpl = parcObject_CreateAndClearInstance(MetisContentStoreInterface);

    if (storeImpl != NULL) {
        storeImpl->_privateData = parcObject_CreateAndClearInstance(_MetisLRUContentStore);

        if (_metisLRUContentStore_Init(storeImpl->_privateData, config, logger)) {
            storeImpl->putContent = &_metisLRUContentStore_PutContent;
            storeImpl->removeContent = &_metisLRUContentStore_RemoveContent;

            storeImpl->matchInterest = &_metisLRUContentStore_MatchInterest;

            storeImpl->getObjectCount = &_metisLRUContentStore_GetObjectCount;
            storeImpl->getObjectCapacity = &_metisLRUContentStore_GetObjectCapacity;

            storeImpl->log = &_metisLRUContentStore_Log;

            storeImpl->acquire = &_metisLRUContentStore_Acquire;
            storeImpl->release = &_metisLRUContentStore_Release;

            // Initialize from the config passed to us.
            _metisLRUContentStore_SetObjectCapacity(storeImpl, config->objectCapacity);

            if (metisLogger_IsLoggable(logger, MetisLoggerFacility_Processor, PARCLogLevel_Info)) {
                metisLogger_Log(logger, MetisLoggerFacility_Processor, PARCLogLevel_Info, __func__,
                                "LRUContentStore %p created with capacity %zu",
                                (void *) storeImpl, metisContentStoreInterface_GetObjectCapacity(storeImpl));
            }
        }
    } else {
        parcObject_Release((void **) &storeImpl);
    }

    return storeImpl;
}
Example #25
0
/*
 * Reading a BPF packet will include the BPF header.  Frame may include the FCS.
 */
bool
metisGenericEther_ReadNextFrame(MetisGenericEther *ether, PARCEventBuffer *readbuffer)
{
    assertNotNull(ether, "Parameter ether must be non-null");
    assertNotNull(readbuffer, "Parameter readbuffer must be non-null");

    bool success = false;

    if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
        metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                        "Workbuffer length %zu", __func__, parcEventBuffer_GetLength(ether->workBuffer));
    }

    // Do we already have something in our work buffer?  If not, try to read something.
    if (parcEventBuffer_GetLength(ether->workBuffer) == 0) {
        _darwinEthernet_ReadSocket(ether);
    }

    success = _darwinEthernet_WorkBufferToReadBuffer(ether, readbuffer);
    return success;
}
Example #26
0
static void
_metisGenericEther_Destroy(MetisGenericEther **etherPtr)
{
    MetisGenericEther *ether = *etherPtr;

    if (metisLogger_IsLoggable(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
        metisLogger_Log(ether->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                        "GenericEther %p destroyed", (void *) ether);
    }

    if (ether->etherSocket > 0) {
        close(ether->etherSocket);
    }

    if (ether->macAddress) {
        parcBuffer_Release(&ether->macAddress);
    }

    metisLogger_Release(&ether->logger);
    parcEventBuffer_Destroy(&ether->workBuffer);
}
Example #27
0
static void
_metisPIT_StoreInTable(MetisStandardPIT *pit, MetisMessage *interestMessage)
{
    MetisMessage *key = metisMessage_Acquire(interestMessage);

    MetisTicks expiryTime = _metisPIT_CalculateLifetime(pit, interestMessage);

    MetisPitEntry *pitEntry = metisPitEntry_Create(key, expiryTime);
    // this is done in metisPitEntry_Create
    //    metisPitEntry_AddIngressId(pitEntry, metisMessage_GetIngressConnectionId(interestMessage));

    metisMatchingRulesTable_AddToBestTable(pit->table, key, pitEntry);

    if (metisLogger_IsLoggable(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
        metisLogger_Log(pit->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                        "Message %p added to PIT (expiry %" PRIu64 ") ingress %u",
                        (void *) interestMessage,
                        metisPitEntry_GetExpiryTime(pitEntry),
                        metisMessage_GetIngressConnectionId(interestMessage));
    }
}
Example #28
0
MetisIoOperations *
metisStreamConnection_AcceptConnection(MetisForwarder *metis, int fd, MetisAddressPair *pair, bool isLocal)
{
    _MetisStreamState *stream = parcMemory_AllocateAndClear(sizeof(_MetisStreamState));
    assertNotNull(stream, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(_MetisStreamState));

    MetisDispatcher *dispatcher = metisForwarder_GetDispatcher(metis);
    PARCEventScheduler *eventBase = metisDispatcher_GetEventScheduler(dispatcher);
    stream->bufferEventVector = parcEventQueue_Create(eventBase, fd, PARCEventQueueOption_CloseOnFree | PARCEventQueueOption_DeferCallbacks);

    stream->metis = metis;
    stream->logger = metisLogger_Acquire(metisForwarder_GetLogger(metis));
    stream->fd = fd;
    stream->id = metisForwarder_GetNextConnectionId(metis);
    stream->addressPair = pair;
    stream->isClosed = false;

    // allocate a connection
    MetisIoOperations *io_ops = parcMemory_AllocateAndClear(sizeof(MetisIoOperations));
    assertNotNull(io_ops, "parcMemory_AllocateAndClear(%zu) returned NULL", sizeof(MetisIoOperations));
    memcpy(io_ops, &_template, sizeof(MetisIoOperations));
    io_ops->closure = stream;

    parcEventQueue_SetCallbacks(stream->bufferEventVector, _conn_readcb, NULL, _conn_eventcb, (void *) io_ops);
    parcEventQueue_Enable(stream->bufferEventVector, PARCEventType_Read);

    metisMessenger_Send(metisForwarder_GetMessenger(stream->metis), metisMissive_Create(MetisMissiveType_ConnectionCreate, stream->id));

    // As we are acceting a connection, we begin in the UP state
    _setConnectionState(stream, true);

    if (metisLogger_IsLoggable(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug)) {
        char *pair_str = metisAddressPair_ToString(pair);
        metisLogger_Log(stream->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__,
                        "StreamConnection %p accept for address pair %s", (void *) stream, pair_str);
        free(pair_str);
    }

    return io_ops;
}
Example #29
0
static void
_logProcessCreateTunnelMessage(MetisConfiguration *config, CPIInterfaceIPTunnel *iptun, PARCLogLevel logLevel, const char *message)
{
    if (metisLogger_IsLoggable(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Info)) {
        const CPIAddress *source = cpiInterfaceIPTunnel_GetSourceAddress(iptun);
        const CPIAddress *destination = cpiInterfaceIPTunnel_GetDestinationAddress(iptun);

        const char *symbolicName = cpiInterfaceIPTunnel_GetSymbolicName(iptun);

        char *sourceString = cpiAddress_ToString(source);
        char *remoteString = cpiAddress_ToString(destination);

        metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Info, __func__,
                        "Add connection %s on %s to %s: %s",
                        symbolicName,
                        sourceString,
                        remoteString,
                        message);

        parcMemory_Deallocate((void **) &sourceString);
        parcMemory_Deallocate((void **) &remoteString);
    }
}
Example #30
0
static bool
_metisLRUContentStore_RemoveLeastUsed(_MetisLRUContentStore *store)
{
    bool result = false;

    if (store->objectCount > 0) {
        MetisLruListEntry *lruEntry = metisLruList_PopTail(store->lru);
        MetisContentStoreEntry *storeEntry =
            (MetisContentStoreEntry *) metisLruList_EntryGetData(lruEntry);

        if (metisLogger_IsLoggable(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug)) {
            metisLogger_Log(store->logger, MetisLoggerFacility_Processor, PARCLogLevel_Debug, __func__,
                            "ContentStore %p evict message %p by LRU (LRU evictions %" PRIu64 ")",
                            (void *) store, (void *) metisContentStoreEntry_GetMessage(storeEntry),
                            store->stats.countLruEvictions);
        }

        _metisLRUContentStore_PurgeStoreEntry(store, storeEntry);

        result = true;
    }
    return result;
}