/* * Copyright 2006-2008, Haiku, Inc. All Rights Reserved. * Distributed under the terms of the MIT License. * * Authors: * Axel Dörfler, axeld@pinc-software.de * Ingo Weinhold, ingo_weinhold@gmx.de */ #include "simple_net_buffer.h" #include "utility.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "paranoia_config.h" //#define TRACE_BUFFER #ifdef TRACE_BUFFER # define TRACE(x) dprintf x #else # define TRACE(x) ; #endif #define MAX_ANCILLARY_DATA_SIZE 128 struct ancillary_data : DoublyLinkedListLinkImpl { void* Data() { return (char*)this + _ALIGN(sizeof(ancillary_data)); } static ancillary_data* FromData(void* data) { return (ancillary_data*)((char*)data - _ALIGN(sizeof(ancillary_data))); } ancillary_data_header header; void (*destructor)(const ancillary_data_header*, void*); }; typedef DoublyLinkedList ancillary_data_list; struct net_buffer_private : simple_net_buffer { ancillary_data_list ancillary_data; }; static status_t append_data(net_buffer *buffer, const void *data, size_t size); static status_t trim_data(net_buffer *_buffer, size_t newSize); static status_t remove_header(net_buffer *_buffer, size_t bytes); static status_t remove_trailer(net_buffer *_buffer, size_t bytes); static void copy_metadata(net_buffer *destination, const net_buffer *source) { memcpy(destination->source, source->source, min_c(source->source->sa_len, sizeof(sockaddr_storage))); memcpy(destination->destination, source->destination, min_c(source->destination->sa_len, sizeof(sockaddr_storage))); destination->flags = source->flags; destination->interface = source->interface; destination->offset = source->offset; destination->size = source->size; destination->protocol = source->protocol; destination->type = source->type; } // #pragma mark - module API static net_buffer * create_buffer(size_t headerSpace) { net_buffer_private *buffer = new(nothrow) net_buffer_private; if (buffer == NULL) return NULL; TRACE(("%ld: create buffer %p\n", find_thread(NULL), buffer)); buffer->data = NULL; new(&buffer->ancillary_data) ancillary_data_list; buffer->source = (sockaddr *)&buffer->storage.source; buffer->destination = (sockaddr *)&buffer->storage.destination; buffer->storage.source.ss_len = 0; buffer->storage.destination.ss_len = 0; buffer->interface = NULL; buffer->offset = 0; buffer->flags = 0; buffer->size = 0; buffer->type = -1; return buffer; } static void free_buffer(net_buffer *_buffer) { net_buffer_private *buffer = (net_buffer_private *)_buffer; free(buffer->data); delete buffer; } /*! Creates a duplicate of the \a buffer. The new buffer does not share internal storage; they are completely independent from each other. */ static net_buffer * duplicate_buffer(net_buffer *_buffer) { net_buffer_private *buffer = (net_buffer_private *)_buffer; net_buffer* duplicate = create_buffer(0); if (duplicate == NULL) return NULL; if (append_data(duplicate, buffer->data, buffer->size) != B_OK) { free_buffer(duplicate); return NULL; } copy_metadata(duplicate, buffer); return duplicate; } /*! Clones the buffer by grabbing another reference to the underlying data. If that data changes, it will be changed in the clone as well. If \a shareFreeSpace is \c true, the cloned buffer may claim the free space in the original buffer as the original buffer can still do. If you are using this, it's your responsibility that only one of the buffers will do this. */ static net_buffer * clone_buffer(net_buffer *_buffer, bool shareFreeSpace) { return duplicate_buffer(_buffer); } /*! Split the buffer at offset, the header data is returned as new buffer. TODO: optimize and avoid making a copy. */ static net_buffer * split_buffer(net_buffer *_from, uint32 offset) { net_buffer_private *from = (net_buffer_private *)_from; if (offset > from->size) return NULL; net_buffer_private* buffer = (net_buffer_private*)create_buffer(0); if (buffer == NULL) return NULL; // allocate space for the tail data size_t remaining = from->size - offset; uint8* tailData = (uint8*)malloc(remaining); if (tailData == NULL) { free_buffer(buffer); return NULL; } memcpy(tailData, from->data + offset, remaining); // truncate original data and move it to the new buffer buffer->data = (uint8*)realloc(from->data, offset); buffer->size = offset; // the old buffer gets the newly allocated tail data from->data = tailData; from->size = remaining; return buffer; } /*! Merges the second buffer with the first. If \a after is \c true, the second buffer's contents will be appended to the first ones, else they will be prepended. The second buffer will be freed if this function succeeds. */ static status_t merge_buffer(net_buffer *_buffer, net_buffer *_with, bool after) { net_buffer_private *buffer = (net_buffer_private *)_buffer; net_buffer_private *with = (net_buffer_private *)_with; if (with == NULL) return B_BAD_VALUE; if (after) { // the simple case: just append the second buffer status_t error = append_data(buffer, with->data, with->size); if (error != B_OK) return error; } else { // append buffer to the second buffer, then switch the data status_t error = append_data(with, buffer->data, buffer->size); if (error != B_OK) return error; free(buffer->data); buffer->data = with->data; buffer->size = with->size; with->data = NULL; } free_buffer(with); return B_OK; } /*! Writes into existing allocated memory. \return B_BAD_VALUE if you write outside of the buffers current bounds. */ static status_t write_data(net_buffer *_buffer, size_t offset, const void *data, size_t size) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (offset + size > buffer->size) return B_BAD_VALUE; if (size == 0) return B_OK; memcpy(buffer->data + offset, data, size); return B_OK; } static status_t read_data(net_buffer *_buffer, size_t offset, void *data, size_t size) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (offset + size > buffer->size) return B_BAD_VALUE; if (size == 0) return B_OK; memcpy(data, buffer->data + offset, size); return B_OK; } static status_t prepend_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer) { if (size == 0) return B_OK; net_buffer_private *buffer = (net_buffer_private *)_buffer; uint8* newData = (uint8*)malloc(buffer->size + size); if (newData == NULL) return B_NO_MEMORY; memcpy(newData + size, buffer->data, buffer->size); free(buffer->data); buffer->data = newData; buffer->size += size; if (_contiguousBuffer != NULL) *_contiguousBuffer = buffer->data; return B_OK; } static status_t prepend_data(net_buffer *buffer, const void *data, size_t size) { status_t status = prepend_size(buffer, size, NULL); if (status < B_OK) return status; write_data(buffer, 0, data, size); return B_OK; } static status_t append_size(net_buffer *_buffer, size_t size, void **_contiguousBuffer) { if (size == 0) return B_OK; net_buffer_private *buffer = (net_buffer_private *)_buffer; uint8* newData = (uint8*)realloc(buffer->data, buffer->size + size); if (newData == NULL) return B_NO_MEMORY; if (_contiguousBuffer != NULL) *_contiguousBuffer = newData + buffer->size; buffer->data = newData; buffer->size += size; return B_OK; } static status_t append_data(net_buffer *buffer, const void *data, size_t size) { size_t used = buffer->size; status_t status = append_size(buffer, size, NULL); if (status < B_OK) return status; write_data(buffer, used, data, size); return B_OK; } /*! Removes bytes from the beginning of the buffer. */ static status_t remove_header(net_buffer *_buffer, size_t bytes) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (bytes > buffer->size) return B_BAD_VALUE; if (bytes == 0) return B_OK; buffer->size -= bytes; memmove(buffer->data, buffer->data + bytes, buffer->size); buffer->data = (uint8*)realloc(buffer->data, buffer->size); return B_OK; } /*! Removes bytes from the end of the buffer. */ static status_t remove_trailer(net_buffer *buffer, size_t bytes) { return trim_data(buffer, buffer->size - bytes); } /*! Trims the buffer to the specified \a newSize by removing space from the end of the buffer. */ static status_t trim_data(net_buffer *_buffer, size_t newSize) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (newSize > buffer->size) return B_BAD_VALUE; if (newSize == buffer->size) return B_OK; buffer->data = (uint8*)realloc(buffer->data, newSize); buffer->size = newSize; return B_OK; } /*! Appends data coming from buffer \a source to the buffer \a buffer. It only clones the data, though, that is the data is not copied, just referenced. */ static status_t append_cloned_data(net_buffer *_buffer, net_buffer *_source, uint32 offset, size_t bytes) { if (bytes == 0) return B_OK; net_buffer_private *buffer = (net_buffer_private *)_buffer; net_buffer_private *source = (net_buffer_private *)_source; if (offset + bytes > source->size) return B_BAD_VALUE; return append_data(buffer, source->data + offset, bytes); } /*! Attaches ancillary data to the given buffer. The data are completely orthogonal to the data the buffer stores. \param buffer The buffer. \param header Description of the data. \param data If not \c NULL, the data are copied into the allocated storage. \param destructor If not \c NULL, this function will be invoked with the data as parameter when the buffer is destroyed. \param _allocatedData Will be set to the storage allocated for the data. \return \c B_OK when everything goes well, another error code otherwise. */ static status_t attach_ancillary_data(net_buffer *_buffer, const ancillary_data_header *header, const void *data, void (*destructor)(const ancillary_data_header*, void*), void **_allocatedData) { // TODO: Obviously it would be nice to allocate the memory for the // ancillary data in the buffer. net_buffer_private *buffer = (net_buffer_private *)_buffer; // check parameters if (header == NULL) return B_BAD_VALUE; if (header->len > MAX_ANCILLARY_DATA_SIZE) return ENOBUFS; // allocate buffer void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + header->len); if (dataBuffer == NULL) return B_NO_MEMORY; // init and attach the structure ancillary_data *ancillaryData = new(dataBuffer) ancillary_data; ancillaryData->header = *header; ancillaryData->destructor = destructor; buffer->ancillary_data.Add(ancillaryData); if (data != NULL) memcpy(ancillaryData->Data(), data, header->len); if (_allocatedData != NULL) *_allocatedData = ancillaryData->Data(); return B_OK; } /*! Detaches ancillary data from the given buffer. The associated memory is free, i.e. the \a data pointer must no longer be used after calling this function. Depending on \a destroy, the destructor is invoked before freeing the data. \param buffer The buffer. \param data Pointer to the data to be removed (as returned by attach_ancillary_data() or next_ancillary_data()). \param destroy If \c true, the destructor, if one was passed to attach_ancillary_data(), is invoked for the data. \return \c B_OK when everything goes well, another error code otherwise. */ static status_t detach_ancillary_data(net_buffer *_buffer, void *data, bool destroy) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (data == NULL) return B_BAD_VALUE; ancillary_data *ancillaryData = ancillary_data::FromData(data); buffer->ancillary_data.Remove(ancillaryData); if (destroy && ancillaryData->destructor != NULL) { ancillaryData->destructor(&ancillaryData->header, ancillaryData->Data()); } free(ancillaryData); return B_OK; } /*! Moves all ancillary data from buffer \c from to the end of the list of ancillary data of buffer \c to. Note, that this is the only function that transfers or copies ancillary data from one buffer to another. \param from The buffer from which to remove the ancillary data. \param to The buffer to which to add teh ancillary data. \return A pointer to the first of the moved ancillary data, if any, \c NULL otherwise. */ static void * transfer_ancillary_data(net_buffer *_from, net_buffer *_to) { net_buffer_private *from = (net_buffer_private *)_from; net_buffer_private *to = (net_buffer_private *)_to; if (from == NULL || to == NULL) return NULL; ancillary_data *ancillaryData = from->ancillary_data.Head(); to->ancillary_data.MoveFrom(&from->ancillary_data); return ancillaryData != NULL ? ancillaryData->Data() : NULL; } /*! Returns the next ancillary data. When iterating over the data, initially a \c NULL pointer shall be passed as \a previousData, subsequently the previously returned data pointer. After the last item, \c NULL is returned. Note, that it is not safe to call detach_ancillary_data() for a data item and then pass that pointer to this function. First get the next item, then detach the previous one. \param buffer The buffer. \param previousData The pointer to the previous data returned by this function. Initially \c NULL shall be passed. \param header Pointer to allocated storage into which the data description is written. May be \c NULL. \return A pointer to the next ancillary data in the buffer. \c NULL after the last one. */ static void* next_ancillary_data(net_buffer *_buffer, void *previousData, ancillary_data_header *_header) { net_buffer_private *buffer = (net_buffer_private *)_buffer; ancillary_data *ancillaryData; if (previousData == NULL) { ancillaryData = buffer->ancillary_data.Head(); } else { ancillaryData = ancillary_data::FromData(previousData); ancillaryData = buffer->ancillary_data.GetNext(ancillaryData); } if (ancillaryData == NULL) return NULL; if (_header != NULL) *_header = ancillaryData->header; return ancillaryData->Data(); } /*! Tries to directly access the requested space in the buffer. If the space is contiguous, the function will succeed and place a pointer to that space into \a _contiguousBuffer. \return B_BAD_VALUE if the offset is outside of the buffer's bounds. \return B_ERROR in case the buffer is not contiguous at that location. */ static status_t direct_access(net_buffer *_buffer, uint32 offset, size_t size, void **_contiguousBuffer) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (offset + size > buffer->size) return B_BAD_VALUE; *_contiguousBuffer = buffer->data + offset; return B_OK; } static int32 checksum_data(net_buffer *_buffer, uint32 offset, size_t size, bool finalize) { net_buffer_private *buffer = (net_buffer_private *)_buffer; if (offset + size > buffer->size || size == 0) return B_BAD_VALUE; uint16 sum = compute_checksum(buffer->data + offset, size); if ((offset & 1) != 0) { // if we're at an uneven offset, we have to swap the checksum sum = __swap_int16(sum); } if (!finalize) return (uint16)sum; return (uint16)~sum; } static uint32 get_iovecs(net_buffer *_buffer, struct iovec *iovecs, uint32 vecCount) { net_buffer_private *buffer = (net_buffer_private *)_buffer; iovecs[0].iov_base = buffer->data; iovecs[0].iov_len = buffer->size; return 1; } static uint32 count_iovecs(net_buffer *_buffer) { return 1; } static void swap_addresses(net_buffer *buffer) { std::swap(buffer->source, buffer->destination); } static void dump_buffer(net_buffer *_buffer) { net_buffer_private *buffer = (net_buffer_private *)_buffer; dprintf("buffer %p, size %ld, data: %p\n", buffer, buffer->size, buffer->data); dump_block((char*)buffer->data, min_c(buffer->size, 32), " "); } static status_t std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: return B_OK; case B_MODULE_UNINIT: return B_OK; default: return B_ERROR; } } net_buffer_module_info gSimpleNetBufferModule = { //net_buffer_module_info gNetBufferModule = { { NET_BUFFER_MODULE_NAME, 0, std_ops }, create_buffer, free_buffer, duplicate_buffer, clone_buffer, split_buffer, merge_buffer, prepend_size, prepend_data, append_size, append_data, NULL, // insert NULL, // remove remove_header, remove_trailer, trim_data, append_cloned_data, NULL, // associate_data attach_ancillary_data, detach_ancillary_data, transfer_ancillary_data, next_ancillary_data, direct_access, read_data, write_data, checksum_data, NULL, // get_memory_map get_iovecs, count_iovecs, swap_addresses, dump_buffer, // dump };