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); }
/** * 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; }
/** * @function startNewMessage * @abstract Peek at the fixed header and set the stream->nextMessageLength * @discussion * This function manipulates the stream->nextMessageLength. After reading a FixedHeader, set nextMessageLength * to the total length of the message. * * PRECONDITION: stream->nextMessageLength == 0 * PRECONDITION: inputBytesAvailable >= FIXED_HEADER_LEN * * @param stream is the stream begin parsed. * @param input is the input PARCEventBuffer (corresponds to the buffer event's input) * @param inputBytesAvailable is the number of bytes available in the input PARCEventBuffer. * @return <#return#> */ static void _startNewMessage(_MetisStreamState *stream, PARCEventBuffer *input, size_t inputBytesAvailable) { assertTrue(stream->nextMessageLength == 0, "Invalid state, nextMessageLength not zero: %zu", stream->nextMessageLength); assertTrue(inputBytesAvailable >= metisTlv_FixedHeaderLength(), "read_length not a whole fixed header!: %zd", inputBytesAvailable); // this linearizes the first FIXED_HEADER_LEN bytes of the input buffer's iovecs and // returns a pointer to it. uint8_t *fh = parcEventBuffer_Pullup(input, metisTlv_FixedHeaderLength()); // Calculate the total message size based on the fixed header stream->nextMessageLength = metisTlv_TotalPacketLength(fh); }
/* * Returns the total legth of the good data: the ethernet header plus ccnx packet. * If there is a FCS, it will be excluded. * * PRECONDITION: You have drained off any BPF headers and the first byte * of the work buffer points to the first byte of the Ethernet header */ static uint16_t _getFrameLengthFromWorkBuffer(MetisGenericEther *ether) { uint16_t frameLength = 0; // read the fixed header uint8_t *etherHeader = parcEventBuffer_Pullup(ether->workBuffer, ETHER_HDR_LEN + metisTlv_FixedHeaderLength()); if (etherHeader) { uint8_t *fixedHeader = etherHeader + ETHER_HDR_LEN; frameLength = metisTlv_TotalPacketLength(fixedHeader) + ETHER_HDR_LEN; } return frameLength; }