root/src/add-ons/kernel/network/stack/simple_net_buffer.cpp
/*
 * 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 <net_buffer.h>
#include <slab/Slab.h>
#include <tracing.h>
#include <util/list.h>

#include <ByteOrder.h>
#include <debug.h>
#include <KernelExport.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>

#include <algorithm>
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>

#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<ancillary_data> {
        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> 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
};