示例#1
0
LONGBOW_TEST_CASE(Global, metisMessage_ReadFromBuffer)
{
    char message_str[] = "\x00Once upon a time, in a stack far away, a dangling pointer found its way to the top of the heap.";

    PARCEventBuffer *buff = parcEventBuffer_Create();
    parcEventBuffer_Append(buff, message_str, sizeof(message_str));

    PARCLogReporter *reporter = parcLogReporterTextStdout_Create();
    MetisLogger *logger = metisLogger_Create(reporter, parcClock_Wallclock());
    parcLogReporter_Release(&reporter);
    MetisMessage *message = metisMessage_ReadFromBuffer(1, 2, buff, sizeof(message_str), logger);
    metisLogger_Release(&logger);

    assertNotNull(message, "Got null from metisMessage_CreateFromBuffer");

    assertTrue(parcEventBuffer_GetLength(message->messageBytes) == sizeof(message_str),
               "Length of internal buffer wrong, expected %zu got %zu",
               sizeof(message_str),
               parcEventBuffer_GetLength(message->messageBytes));

    uint8_t *p = parcEventBuffer_Pullup(message->messageBytes, sizeof(message_str));
    assertTrue(memcmp(p, message_str, sizeof(message_str)) == 0, "Internal buffer contents does not match test");
    assertTrue(message->ingressConnectionId == 1, "IngressConnectionId wrong, expected %d got %u", 1, message->ingressConnectionId);
    assertTrue(message->receiveTime == 2, "receiveTime wrong, expected %u got %" PRIu64, 2, message->receiveTime);
    assertTrue(parcEventBuffer_GetLength(buff) == 0, "Origin buffer not drained, expected 0, got %zu", parcEventBuffer_GetLength(buff));

    metisMessage_Release(&message);
    parcEventBuffer_Destroy(&buff);
}
示例#2
0
/**
 * @function conn_readcb
 * @abstract Event callback for reads
 * @discussion
 *   Will read messages off the input.  Continues reading as long as we
 *   can get a header to determine the next message length or as long as we
 *   can read a complete message.
 *
 *   This function manipulates the read low water mark.  (1) read a fixed header plus complete message,
 *   then set the low water mark to FIXED_HEADER_LEN.  (2) read a fixed header, but not a complete
 *   message, then set low water mark to the total mesage length.  Using the low water mark like this
 *   means the buffer event will only trigger on meaningful byte boundaries when we can get actual
 *   work done.
 *
 * @param <#param1#>
 * @return <#return#>
 */
static void
_conn_readcb(PARCEventQueue *event, PARCEventType type, void *ioOpsVoid)
{
    MetisIoOperations *ops = (MetisIoOperations *) ioOpsVoid;
    _MetisStreamState *stream = (_MetisStreamState *) metisIoOperations_GetClosure(ops);

    PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(event);

    // drain the input buffer
    while (parcEventBuffer_GetLength(input) >= metisTlv_FixedHeaderLength() && parcEventBuffer_GetLength(input) >= stream->nextMessageLength) {
        // this may set the stream->nextMessageLength
        MetisMessage *message = _single_read(input, stream);

        if (message) {
            metisForwarder_Receive(stream->metis, message);
        }
    }

    if (stream->nextMessageLength == 0) {
        // we don't have the next header, so set it to the header length
        metisStreamBuffer_SetWatermark(event, true, false, metisTlv_FixedHeaderLength(), 0);
    } else {
        // set it to the packet length
        metisStreamBuffer_SetWatermark(event, true, false, stream->nextMessageLength, 0);
    }
    parcEventBuffer_Destroy(&input);
}
示例#3
0
static void
_metisCliSession_ReadCallback(PARCEventQueue *event, PARCEventType type, void *cliSessionVoid)
{
    assertTrue(type == PARCEventType_Read, "Illegal type: expected read event, got %d\n", type);
    _MetisCommandLineInterface_Session *session = (_MetisCommandLineInterface_Session *) cliSessionVoid;
    PARCEventBuffer *input = parcEventBuffer_GetQueueBufferInput(event);

    while (parcEventBuffer_GetLength(input) > 0) {
        size_t readLength = 0;
        char *cmdline = parcEventBuffer_ReadLine(input, &readLength);
        if (cmdline == NULL) {
            // we have run out of input, we're done
            parcEventBuffer_Destroy(&input);
            return;
        }

        // we have a whole command line
        bool success = _metisCliSession_ProcessCommand(session, cmdline);
        parcEventBuffer_FreeLine(input, &cmdline);

        if (!success) {
            // the session is dead
            parcEventBuffer_Destroy(&input);
            return;
        }

        _metisCliSession_DisplayPrompt(session);
    }
    parcEventBuffer_Destroy(&input);
}
示例#4
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;
}
示例#5
0
bool
metisGenericEther_SendFrame(MetisGenericEther *ether, PARCEventBuffer *buffer)
{
    assertNotNull(ether, "Parameter ether must be non-null");

    size_t length = parcEventBuffer_GetLength(buffer);
    int written = parcEventBuffer_WriteToFileDescriptor(buffer, ether->etherSocket, -1);
    if (written == length) {
        return true;
    }
    return false;
}
示例#6
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;
}
TransportMessage *
rtaComponent_GetMessage(PARCEventQueue *queue)
{
    PARCEventBuffer *in = parcEventBuffer_GetQueueBufferInput(queue);

    while (parcEventBuffer_GetLength(in) >= sizeof(TransportMessage *)) {
        ssize_t len;
        TransportMessage *tm;
        RtaConnection *conn;

        len = parcEventBuffer_Read(in, (void *)&tm, sizeof(&tm));

        assertTrue(len == sizeof(TransportMessage *),
                   "parcEventBuffer_Read returned error");

        // Is the transport message for an open connection?
        conn = rtaConnection_GetFromTransport(tm);
        assertNotNull(conn, "%s GetInfo returnd null connection\n", __func__);

        if (DEBUG_OUTPUT) {
            printf("%s queue %-12s tm %p\n",
                   __func__,
                   rtaProtocolStack_GetQueueName(rtaConnection_GetStack(conn), queue),
                   (void *) tm);
        }

        (void) rtaConnection_DecrementMessagesInQueue(conn);

        if (rtaConnection_GetState(conn) != CONN_CLOSED) {
            parcEventBuffer_Destroy(&in);
            return tm;
        }

        // it's a closed connection

        if (DEBUG_OUTPUT) {
            printf("%s clearing connection %p reference in transport\n",
                   __func__, (void *) conn);
        }

        // should increment a drop counter (case 908)
        transportMessage_Destroy(&tm);
    }

    parcEventBuffer_Destroy(&in);
    return NULL;
}
示例#8
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;
}
示例#9
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;
}