#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"
#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;
}
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;
}
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;
}
static net_buffer *
clone_buffer(net_buffer *_buffer, bool shareFreeSpace)
{
return duplicate_buffer(_buffer);
}
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;
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);
buffer->data = (uint8*)realloc(from->data, offset);
buffer->size = offset;
from->data = tailData;
from->size = remaining;
return buffer;
}
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) {
status_t error = append_data(buffer, with->data, with->size);
if (error != B_OK)
return error;
} else {
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;
}
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;
}
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;
}
static status_t
remove_trailer(net_buffer *buffer, size_t bytes)
{
return trim_data(buffer, buffer->size - bytes);
}
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;
}
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);
}
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)
{
net_buffer_private *buffer = (net_buffer_private *)_buffer;
if (header == NULL)
return B_BAD_VALUE;
if (header->len > MAX_ANCILLARY_DATA_SIZE)
return ENOBUFS;
void *dataBuffer = malloc(_ALIGN(sizeof(ancillary_data)) + header->len);
if (dataBuffer == NULL)
return B_NO_MEMORY;
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;
}
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;
}
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;
}
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();
}
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) {
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_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,
NULL,
remove_header,
remove_trailer,
trim_data,
append_cloned_data,
NULL,
attach_ancillary_data,
detach_ancillary_data,
transfer_ancillary_data,
next_ancillary_data,
direct_access,
read_data,
write_data,
checksum_data,
NULL,
get_iovecs,
count_iovecs,
swap_addresses,
dump_buffer,
};