diff --git a/headers/private/net/net_buffer.h b/headers/private/net/net_buffer.h index f269f37f28..d5fcf30081 100644 --- a/headers/private/net/net_buffer.h +++ b/headers/private/net/net_buffer.h @@ -37,6 +37,12 @@ typedef struct net_buffer { uint8 protocol; } net_buffer; +typedef struct ancillary_data_header { + int level; + int type; + size_t len; +} ancillary_data_header; + struct net_buffer_module_info { module_info info; @@ -68,6 +74,17 @@ struct net_buffer_module_info { status_t (*associate_data)(net_buffer *buffer, void *data); + 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); + status_t (*detach_ancillary_data)(net_buffer *buffer, void *data, + bool destroy); + void * (*transfer_ancillary_data)(net_buffer *from, + net_buffer *to); + void * (*next_ancillary_data)(net_buffer *buffer, + void *previousData, ancillary_data_header *_header); + status_t (*direct_access)(net_buffer *buffer, uint32 offset, size_t bytes, void **_data); status_t (*read)(net_buffer *buffer, uint32 offset, void *data, diff --git a/src/add-ons/kernel/network/stack/net_buffer.cpp b/src/add-ons/kernel/network/stack/net_buffer.cpp index cf86804846..bd63907078 100644 --- a/src/add-ons/kernel/network/stack/net_buffer.cpp +++ b/src/add-ons/kernel/network/stack/net_buffer.cpp @@ -16,6 +16,7 @@ #include <ByteOrder.h> #include <KernelExport.h> #include <util/AutoLock.h> +#include <util/DoublyLinkedList.h> #include <algorithm> #include <stdlib.h> @@ -58,11 +59,31 @@ struct data_header { data_node *first_node; }; +#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; + #define MAX_FREE_BUFFER_SIZE (BUFFER_SIZE - sizeof(data_header)) struct net_buffer_private : net_buffer { - struct list buffers; - data_node first_node; + struct list buffers; + data_node first_node; + ancillary_data_list ancillary_data; struct { struct sockaddr_storage source; @@ -353,6 +374,8 @@ create_buffer(size_t headerSpace) list_init(&buffer->buffers); list_add_item(&buffer->buffers, &buffer->first_node); + new(&buffer->ancillary_data) ancillary_data_list; + buffer->source = (sockaddr *)&buffer->storage.source; buffer->destination = (sockaddr *)&buffer->storage.destination; @@ -382,6 +405,12 @@ free_buffer(net_buffer *_buffer) remove_data_node(node); } + while (ancillary_data* data = buffer->ancillary_data.RemoveHead()) { + if (data->destructor != NULL) + data->destructor(&data->header, data->Data()); + free(data); + } + free_net_buffer(buffer); } @@ -1067,6 +1096,160 @@ append_cloned_data(net_buffer *_buffer, net_buffer *_source, uint32 offset, } +/*! + 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 @@ -1257,6 +1440,11 @@ net_buffer_module_info gNetBufferModule = { NULL, // associate_data + attach_ancillary_data, + detach_ancillary_data, + transfer_ancillary_data, + next_ancillary_data, + direct_access, read_data, write_data,