/* * We've reach the END fragment of the reassembly buffer. * 1) Make a metis message out of the reassembly buffer, * 2) put the message in the receive queue (discard if queue full) * 3) allocate a new reassembly buffer * 4) reset the parser */ static void _finalizeReassemblyBuffer(MetisHopByHopFragmenter *fragmenter) { // This takes ownership of fragmenter->currentReceiveBuffer MetisMessage *reassembled = metisMessage_CreateFromBuffer(fragmenter->currentReceiveBufferIngressId, fragmenter->currentReceiveBufferStartTicks, fragmenter->currentReceiveBuffer, fragmenter->logger); if (reassembled) { bool success = parcRingBuffer1x1_Put(fragmenter->receiveQueue, reassembled); if (success) { metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__, "Fragmenter %p putting reassembed message %p in receive queue", (void *) fragmenter, (void *) reassembled); } else { metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__, "Fragmenter %p failed to put reassembled message in receive queue, dropping", (void *) fragmenter); metisMessage_Release(&reassembled); } fragmenter->currentReceiveBuffer = parcEventBuffer_Create(); } else { metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__, "Fragmenter %p failed to parse reassembled packet to MetisMessage, dropping", (void *) fragmenter); } _resetParser(fragmenter); }
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; }
/** * Add an IP-based tunnel. * * The call cal fail if the symbolic name is a duplicate. It could also fail if there's an problem creating * the local side of the tunnel (i.e. the local socket address is not usable). * * @return true Tunnel added * @return false Tunnel not added (an error) */ static CCNxControl * metisConfiguration_ProcessCreateTunnel(MetisConfiguration *config, CCNxControl *control, unsigned ingressId) { bool success = false; CPIInterfaceIPTunnel *iptun = cpiLinks_CreateIPTunnelFromControlMessage(control); const char *symbolicName = cpiInterfaceIPTunnel_GetSymbolicName(iptun); if (!metisSymbolicNameTable_Exists(config->symbolicNameTable, symbolicName)) { const CPIAddress *source = cpiInterfaceIPTunnel_GetSourceAddress(iptun); const CPIAddress *destination = cpiInterfaceIPTunnel_GetDestinationAddress(iptun); MetisIoOperations *ops = NULL; switch (cpiInterfaceIPTunnel_GetTunnelType(iptun)) { case IPTUN_TCP: ops = metisTcpTunnel_Create(config->metis, source, destination); break; case IPTUN_UDP: ops = metisUdpTunnel_Create(config->metis, source, destination); break; case IPTUN_GRE: metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__, "Unsupported tunnel protocol: GRE"); break; default: metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__, "Unsupported tunnel protocol: %d", cpiInterfaceIPTunnel_GetTunnelType(iptun)); break; } if (ops != NULL) { MetisConnection *conn = metisConnection_Create(ops); metisConnectionTable_Add(metisForwarder_GetConnectionTable(config->metis), conn); metisSymbolicNameTable_Add(config->symbolicNameTable, symbolicName, metisConnection_GetConnectionId(conn)); success = true; _logProcessCreateTunnelMessage(config, iptun, PARCLogLevel_Info, "success"); } else { _logProcessCreateTunnelMessage(config, iptun, PARCLogLevel_Warning, "failed, could not create IoOperations"); } } else { _logProcessCreateTunnelMessage(config, iptun, PARCLogLevel_Warning, "failed, symbolic name exists"); } // send the ACK or NACK CCNxControl *response; if (success) { response = _createAck(config, control, ingressId); } else { response = _createNack(config, control, ingressId); } cpiInterfaceIPTunnel_Release(&iptun); return response; }
/* * Parser is in Idle state. We can only accept a B or BE frame. * 1) If B frame: * 1a) append to current receive buffer * 1b) set parser state to Busy * 1c) set the currentReceiveBufferStartTicks * 1d) set the currentReceiveBufferIngressId * 2) If BE frame, do B actions and finalize it (side effect: will reset state to Idle) * 3) Otherwise ignore it * * Precondition: You know that the parser is in the Idle state */ static void _receiveInIdleState(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message, const _HopByHopHeader *fixedHeader) { trapUnexpectedStateIf(fragmenter->parserState != _ParserState_Idle, "Parser in wrong state, expected %d got %d", _ParserState_Idle, fragmenter->parserState); if (_hopByHopHeader_GetBFlag(fixedHeader)) { // start a new packet fragmenter->currentReceiveBufferStartTicks = metisMessage_GetReceiveTime(message); fragmenter->currentReceiveBufferIngressId = metisMessage_GetIngressConnectionId(message); fragmenter->parserState = _ParserState_Busy; _appendFragmentToReassemblyBuffer(fragmenter, message); if (_hopByHopHeader_GetEFlag(fixedHeader)) { // it is also the last fragment _finalizeReassemblyBuffer(fragmenter); } } else if (_hopByHopHeader_GetIFlag(fixedHeader)) { // nothing to do metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__, "Fragmenter %p idle frame, ignorning", (void *) fragmenter); } else { // nothing we can do with this frame metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__, "Fragmenter %p received bad header flags (0x%02X), ignorning", (void *) fragmenter, _hopByHopHeader_GetFlags(fixedHeader)); } }
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(©); 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; }
/** * 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; }
/** * Apply the sequence number rules * * a) If the sequence number is in order, no action. * b) If the sequence number is out of order, reset the parser. * c) Update the next expected sequence number to be this packet's seqnum + 1. * * @param [in] fragmenter An allocated MetisHopByHopFragmenter * @param [in] fixedHeader The packet's fixed header * * Example: * @code * <#example#> * @endcode */ static void _applySequenceNumberRules(MetisHopByHopFragmenter *fragmenter, const _HopByHopHeader *fixedHeader) { uint32_t segnum = _hopByHopHeader_GetSeqnum(fixedHeader); int compare = _compareSequenceNumbers(segnum, fragmenter->nextReceiveFragSequenceNumber); if (compare == 0) { // In order metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__, "Fragmenter %p in-order seqnum %u", (void *) fragmenter, segnum); } else if (compare < 0) { // it is an old sequence number metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__, "Fragmenter %p out-of-order seqnum %u expecting %u", (void *) fragmenter, segnum, fragmenter->nextReceiveFragSequenceNumber); _resetParser(fragmenter); } else if (compare > 0) { // lost packets metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Info, __func__, "Fragmenter %p out-of-order seqnum %u expecting %u", (void *) fragmenter, segnum, fragmenter->nextReceiveFragSequenceNumber); _resetParser(fragmenter); } // the next seqnum we expect will be 1 after what we just received. For example, if we lost packets // this will put us back in line with the new series. fragmenter->nextReceiveFragSequenceNumber = _incrementSequenceNumber(segnum, SEQNUM_MASK); }
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; }
/** * Fragments a message and puts all the fragments in the send queue * * Splits up the message in to fragments. The frist fragment will have the B flag and * the last fragment will have the E flag. If the message fits in one fragment, it will * have both the BE flags. Middle messages have no flags. * * @param [in] fragmenter An allocated MetisHopByHopFragmenter * @param [in] message The message to fragment down to MTU size * * @return true Message was fragmented and all fragments put on send queue * @return false Error durring fragmentation (likley full send queue) * * Example: * @code * { * <#example#> * } * @endcode */ static bool _sendFragments(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message) { const size_t length = metisMessage_Length(message); size_t offset = 0; const size_t maxPayload = fragmenter->mtu - sizeof(_HopByHopHeader); _HopByHopHeader header; memset(&header, 0, sizeof(header)); _hopByHopHeader_SetBFlag(&header); while (offset < length) { size_t payloadLength = maxPayload; const size_t remaining = length - offset; if (remaining < maxPayload) { payloadLength = remaining; _hopByHopHeader_SetEFlag(&header); } const size_t packetLength = sizeof(_HopByHopHeader) + payloadLength; header.version = 1; header.packetType = METIS_PACKET_TYPE_HOPFRAG; header.packetLength = htons(packetLength); header.headerLength = 8; header.tlvType = htons(T_HOPFRAG_PAYLOAD); header.tlvLength = htons(payloadLength); uint32_t seqnum = _nextSendSequenceNumber(fragmenter); _hopByHopHeader_SetSeqnum(&header, seqnum); MetisMessage *fragment = metisMessage_Slice(message, offset, payloadLength, sizeof(header), (uint8_t *) &header); bool goodput = parcRingBuffer1x1_Put(fragmenter->sendQueue, fragment); if (!goodput) { metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__, "Fragmenter %p message %p send queue full offset %zu length %zu", (void *) fragmenter, (void *) message, offset, payloadLength); metisMessage_Release(&fragment); break; } metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__, "Fragmenter %p message %p send queue fragment %p offset %zu length %zu", (void *) fragmenter, (void *) message, (void *) fragment, offset, payloadLength); offset += payloadLength; memset(&header, 0, sizeof(header)); } return (offset == length); }
// 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; }
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 */ }
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(ðer); } 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; }
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; }
static CCNxControl * metisConfiguration_ProcessAddConnectionEthernet(MetisConfiguration *config, CCNxControl *control, unsigned ingressId) { bool success = false; CPIConnectionEthernet *etherConn = cpiConnectionEthernet_FromControl(control); assertNotNull(etherConn, "Control message did not parse to CPIConnectionEthernet"); if (cpiConnectionEthernet_GetEthertype(etherConn) == ETHERTYPE) { CPIAddress *linkAddress = metisSystem_GetMacAddressByName(config->metis, cpiConnectionEthernet_GetInterfaceName(etherConn)); if (linkAddress != NULL) { MetisListenerSet *listenerSet = metisForwarder_GetListenerSet(config->metis); MetisListenerOps *listenerOps = metisListenerSet_Find(listenerSet, METIS_ENCAP_ETHER, linkAddress); if (listenerOps) { // Now add the connection success = _metisConfiguration_AddConnectionEthernet(config, etherConn, linkAddress, listenerOps); } else { char *str = cpiAddress_ToString(linkAddress); metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__, "Could not find listener for interface '%s' addr %s ethertype 0x%04x", cpiConnectionEthernet_GetInterfaceName(etherConn), str, cpiConnectionEthernet_GetEthertype(etherConn)); parcMemory_Deallocate((void **) &str); } cpiAddress_Destroy(&linkAddress); } else { metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__, "Could not resolve interface '%s' to a MAC address", cpiConnectionEthernet_GetInterfaceName(etherConn)); } } else { metisLogger_Log(config->logger, MetisLoggerFacility_Config, PARCLogLevel_Error, __func__, "Control message asked for ethertype %04x, only %04x supported", cpiConnectionEthernet_GetEthertype(etherConn), ETHERTYPE); } CCNxControl *response; if (success) { response = _createAck(config, control, ingressId); } else { response = _createNack(config, control, ingressId); } cpiConnectionEthernet_Release(ðerConn); return response; }
/** * Receives a fragment and applies the protocol algorithm * * 1) A receiver maintains one reassembly queue per peer. * 2) Discard Idle fragments. * 3) Discard fragments until a 'B' fragment is received. Store the received sequence number for this sender. * 4) If an out-of-order fragment is received next, discard the reassembly buffer and go to step (2). * 5) Continue receiving in-order fragments until the first 'E’ fragment. At this time, the fragmented * packet is fully re-assembled and may be passed on to the next layer. * 6) The receiver cannot assume it will receive the 'E' fragment or a subsequence 'I' frame, so it should * use a timeout mechanism appropriate to the link to release allocated memory resources. * * @param [in] fragmenter An allocated MetisHopByHopFragmenter * @param [in] message The fragment packet * * Example: * @code * { * <#example#> * } * @endcode */ static void _receiveFragment(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message) { const _HopByHopHeader *fixedHeader = (const _HopByHopHeader *) metisMessage_FixedHeader(message); _applySequenceNumberRules(fragmenter, fixedHeader); // ====== // Now apply the receiver rules switch (fragmenter->parserState) { case _ParserState_Idle: _receiveInIdleState(fragmenter, message, fixedHeader); break; case _ParserState_Busy: _receiveInBusyState(fragmenter, message, fixedHeader); break; default: metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Error, __func__, "Fragmenter %p Invalid parser state, discarding current buffer and resetting to Idle: %d", (void *) fragmenter, fragmenter->parserState); break; } }
/* * In the Busy state, we can only accept a packet with no flag (middle) or end flag (end of packet). * Anything else is an error and will cause the parser to be reset. * * 1) If no flags * 1a) append to reassembly buffer * 2) If E flag * 2a) append to reassembly buffer * 2b) finalize the buffer (side effect: will reset the parser and place in receive queue) * 3) Otherwise, its an error, reset the parser * * PRECONDITION: you know the packet is in-order relative to the assembly buffer. * This is handled by calling _applySequenceNumberRules() before this function. * PRECONDITION: you know the parser is in the Busy state. */ static void _receiveInBusyState(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message, const _HopByHopHeader *fixedHeader) { trapUnexpectedStateIf(fragmenter->parserState != _ParserState_Busy, "Parser in wrong state, expected %d got %d", _ParserState_Busy, fragmenter->parserState); if (_hopByHopHeader_GetFlags(fixedHeader) == 0) { // It's a middle packet _appendFragmentToReassemblyBuffer(fragmenter, message); } else if (_hopByHopHeader_GetEFlag(fixedHeader)) { // It is the last fragment _appendFragmentToReassemblyBuffer(fragmenter, message); _finalizeReassemblyBuffer(fragmenter); } else { // nothing we can do with this frame, and it's a state machine error metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__, "Fragmenter %p received invalid headers (0x%02X) in Busy state, resetting", (void *) fragmenter, _hopByHopHeader_GetFlags(fixedHeader)); _resetParser(fragmenter); } }
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); }
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; }
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; }
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; }
static void _appendFragmentToReassemblyBuffer(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message) { size_t length = metisMessage_AppendFragmentPayload(message, fragmenter->currentReceiveBuffer); metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Debug, __func__, "Fragmenter %p append %zu bytes to reassembly buffer", (void *) fragmenter, length); }
/** * @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; }
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); } } }
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; }
/** * 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; }
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); } }
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); } }
/** * @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; }
bool metisHopByHopFragmenter_Receive(MetisHopByHopFragmenter *fragmenter, const MetisMessage *message) { if (metisMessage_GetType(message) == MetisMessagePacketType_HopByHopFrag) { _receiveFragment(fragmenter, message); } else { // put the whole thing on the output queue MetisMessage *copy = metisMessage_Acquire(message); bool putSuccess = parcRingBuffer1x1_Put(fragmenter->receiveQueue, copy); if (!putSuccess) { metisMessage_Release(©); metisLogger_Log(fragmenter->logger, MetisLoggerFacility_IO, PARCLogLevel_Warning, __func__, "Failed to add message %p to receive 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 receive queue", (void *) message); } } } // the maximum remaining is its capacity - 1 return (parcRingBuffer1x1_Remaining(fragmenter->receiveQueue) < fragmenter->receiveQueueCapacity - 1); }
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); }