* Implemented a way to preserve header data while passing along a buffer to the
upper layers: you use the store_header() function to mark the header you want to preserve. All subsequent remove_header() calls won't claim the actual data, but only move the node start around. * This header can then be restored by restore_header(). However, a call to prepend_data() will destroy the stored header. Also, if remove_header() cuts off a whole node, restoring the header won't succeed anymore. * Discarded the no longer needed net_buffer::network_header field. * Also discarded the hoplimit field which temporarily breaks the IPv6 build until Atis reworks it. * IPv4 now also dumps the IP header in the send path if debug output is enabled. * icmp_error_reply() might be called so early that the net_buffer's addresses do not point to the reply address; this is now detected, and the addresses are taken out of the IP header in that case. * Improved dumping the net_buffer to also include its address, and flags. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37688 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -16,31 +16,27 @@
|
||||
|
||||
|
||||
typedef struct net_buffer {
|
||||
struct list_link link;
|
||||
struct list_link link;
|
||||
|
||||
// TODO: we should think about moving the address fields into the buffer
|
||||
// data itself via associated data or something like this. Or this
|
||||
// structure as a whole, too...
|
||||
|
||||
struct sockaddr *source;
|
||||
struct sockaddr *destination;
|
||||
struct net_interface *interface;
|
||||
int32 type;
|
||||
struct sockaddr* source;
|
||||
struct sockaddr* destination;
|
||||
struct net_interface* interface;
|
||||
int32 type;
|
||||
union {
|
||||
struct {
|
||||
uint16 start;
|
||||
uint16 end;
|
||||
} fragment;
|
||||
uint32 sequence;
|
||||
uint32 offset;
|
||||
uint16 start;
|
||||
uint16 end;
|
||||
} fragment;
|
||||
uint32 sequence;
|
||||
uint32 offset;
|
||||
};
|
||||
uint32 flags;
|
||||
uint32 size;
|
||||
uint8 protocol;
|
||||
|
||||
// TODO: these two should go away again
|
||||
uint8 hoplimit;
|
||||
void * network_header;
|
||||
uint32 flags;
|
||||
uint32 size;
|
||||
uint8 protocol;
|
||||
} net_buffer;
|
||||
|
||||
struct ancillary_data_container;
|
||||
@@ -48,58 +44,65 @@ struct ancillary_data_container;
|
||||
struct net_buffer_module_info {
|
||||
module_info info;
|
||||
|
||||
net_buffer * (*create)(size_t headerSpace);
|
||||
void (*free)(net_buffer *buffer);
|
||||
net_buffer* (*create)(size_t headerSpace);
|
||||
void (*free)(net_buffer* buffer);
|
||||
|
||||
net_buffer * (*duplicate)(net_buffer *from);
|
||||
net_buffer * (*clone)(net_buffer *from, bool shareFreeSpace);
|
||||
net_buffer * (*split)(net_buffer *from, uint32 offset);
|
||||
status_t (*merge)(net_buffer *buffer, net_buffer *with, bool after);
|
||||
net_buffer* (*duplicate)(net_buffer* from);
|
||||
net_buffer* (*clone)(net_buffer* from, bool shareFreeSpace);
|
||||
net_buffer* (*split)(net_buffer* from, uint32 offset);
|
||||
status_t (*merge)(net_buffer* buffer, net_buffer* with, bool after);
|
||||
|
||||
status_t (*prepend_size)(net_buffer *buffer, size_t size,
|
||||
void **_contiguousBuffer);
|
||||
status_t (*prepend)(net_buffer *buffer, const void *data,
|
||||
status_t (*prepend_size)(net_buffer* buffer, size_t size,
|
||||
void** _contiguousBuffer);
|
||||
status_t (*prepend)(net_buffer* buffer, const void* data,
|
||||
size_t bytes);
|
||||
status_t (*append_size)(net_buffer *buffer, size_t size,
|
||||
void **_contiguousBuffer);
|
||||
status_t (*append)(net_buffer *buffer, const void *data,
|
||||
status_t (*append_size)(net_buffer* buffer, size_t size,
|
||||
void** _contiguousBuffer);
|
||||
status_t (*append)(net_buffer* buffer, const void* data,
|
||||
size_t bytes);
|
||||
status_t (*insert)(net_buffer *buffer, uint32 offset,
|
||||
const void *data, size_t bytes, uint32 flags);
|
||||
status_t (*remove)(net_buffer *buffer, uint32 offset,
|
||||
status_t (*insert)(net_buffer* buffer, uint32 offset,
|
||||
const void* data, size_t bytes, uint32 flags);
|
||||
status_t (*remove)(net_buffer* buffer, uint32 offset,
|
||||
size_t bytes);
|
||||
status_t (*remove_header)(net_buffer *buffer, size_t bytes);
|
||||
status_t (*remove_trailer)(net_buffer *buffer, size_t bytes);
|
||||
status_t (*trim)(net_buffer *buffer, size_t newSize);
|
||||
status_t (*append_cloned)(net_buffer *buffer, net_buffer *source,
|
||||
status_t (*remove_header)(net_buffer* buffer, size_t bytes);
|
||||
status_t (*remove_trailer)(net_buffer* buffer, size_t bytes);
|
||||
status_t (*trim)(net_buffer* buffer, size_t newSize);
|
||||
status_t (*append_cloned)(net_buffer* buffer, net_buffer* source,
|
||||
uint32 offset, size_t bytes);
|
||||
|
||||
status_t (*associate_data)(net_buffer *buffer, void *data);
|
||||
status_t (*associate_data)(net_buffer* buffer, void* data);
|
||||
|
||||
void (*set_ancillary_data)(net_buffer *buffer,
|
||||
struct ancillary_data_container *container);
|
||||
struct ancillary_data_container* (*get_ancillary_data)(net_buffer *buffer);
|
||||
void * (*transfer_ancillary_data)(net_buffer *from,
|
||||
net_buffer *to);
|
||||
void (*set_ancillary_data)(net_buffer* buffer,
|
||||
struct ancillary_data_container* container);
|
||||
struct ancillary_data_container* (*get_ancillary_data)(net_buffer* buffer);
|
||||
void* (*transfer_ancillary_data)(net_buffer* from,
|
||||
net_buffer* to);
|
||||
|
||||
status_t (*direct_access)(net_buffer *buffer, uint32 offset,
|
||||
size_t bytes, void **_data);
|
||||
status_t (*read)(net_buffer *buffer, uint32 offset, void *data,
|
||||
status_t (*store_header)(net_buffer* buffer);
|
||||
ssize_t (*stored_header_length)(net_buffer* buffer);
|
||||
status_t (*restore_header)(net_buffer* buffer, uint32 offset,
|
||||
void* data, size_t bytes);
|
||||
status_t (*append_restored_header)(net_buffer* buffer,
|
||||
net_buffer* source, uint32 offset, size_t bytes);
|
||||
|
||||
status_t (*direct_access)(net_buffer* buffer, uint32 offset,
|
||||
size_t bytes, void** _data);
|
||||
status_t (*read)(net_buffer* buffer, uint32 offset, void* data,
|
||||
size_t bytes);
|
||||
status_t (*write)(net_buffer *buffer, uint32 offset,
|
||||
const void *data, size_t bytes);
|
||||
status_t (*write)(net_buffer* buffer, uint32 offset,
|
||||
const void* data, size_t bytes);
|
||||
|
||||
int32 (*checksum)(net_buffer *buffer, uint32 offset, size_t bytes,
|
||||
int32 (*checksum)(net_buffer* buffer, uint32 offset, size_t bytes,
|
||||
bool finalize);
|
||||
status_t (*get_memory_map)(net_buffer *buffer,
|
||||
struct iovec *iovecs, uint32 vecCount);
|
||||
uint32 (*get_iovecs)(net_buffer *buffer,
|
||||
struct iovec *iovecs, uint32 vecCount);
|
||||
uint32 (*count_iovecs)(net_buffer *buffer);
|
||||
status_t (*get_memory_map)(net_buffer* buffer,
|
||||
struct iovec* iovecs, uint32 vecCount);
|
||||
uint32 (*get_iovecs)(net_buffer* buffer,
|
||||
struct iovec* iovecs, uint32 vecCount);
|
||||
uint32 (*count_iovecs)(net_buffer* buffer);
|
||||
|
||||
void (*swap_addresses)(net_buffer *buffer);
|
||||
void (*swap_addresses)(net_buffer* buffer);
|
||||
|
||||
void (*dump)(net_buffer *buffer);
|
||||
void (*dump)(net_buffer* buffer);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
#include <NetBufferUtilities.h>
|
||||
#include <NetUtilities.h>
|
||||
|
||||
#include "ipv4.h"
|
||||
|
||||
@@ -97,6 +98,16 @@ get_domain(struct net_buffer* buffer)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
fill_sockaddr_in(sockaddr_in* target, in_addr_t address)
|
||||
{
|
||||
target->sin_family = AF_INET;
|
||||
target->sin_len = sizeof(sockaddr_in);
|
||||
target->sin_port = 0;
|
||||
target->sin_addr.s_addr = address;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
is_icmp_error(uint8 type)
|
||||
{
|
||||
@@ -403,38 +414,25 @@ icmp_error_reply(net_protocol* protocol, net_buffer* buffer, uint32 code,
|
||||
icmp_decode(code, icmpType, icmpCode);
|
||||
TRACE(" icmp type %u, code %u\n", icmpType, icmpCode);
|
||||
|
||||
NetBufferHeaderReader<ipv4_header> bufferHeader(buffer);
|
||||
size_t offset = 0;
|
||||
ipv4_header header;
|
||||
if (gBufferModule->restore_header(buffer, 0, &header, sizeof(ipv4_header))
|
||||
!= B_OK)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// TODO: get rid of network_header
|
||||
ipv4_header* header = (ipv4_header*)buffer->network_header;
|
||||
if (header == NULL) {
|
||||
if (bufferHeader.Status() != B_OK)
|
||||
return bufferHeader.Status();
|
||||
|
||||
header = &bufferHeader.Data();
|
||||
offset = header->HeaderLength();
|
||||
// Check if we actually have an IPv4 header now
|
||||
if (header.version != IPV4_VERSION
|
||||
|| header.HeaderLength() < sizeof(ipv4_header)) {
|
||||
TRACE(" no IPv4 header found\n");
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
char originalData[64];
|
||||
size_t originalSize
|
||||
= std::min(buffer->size - offset, sizeof(originalData));
|
||||
if (originalSize < 8) {
|
||||
// We need at least 8 bytes of data of the original data
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
status_t status = gBufferModule->read(buffer, offset, originalData,
|
||||
originalSize);
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// RFC 1122 3.2.2:
|
||||
// ICMP error message should not be sent on reception of
|
||||
// an ICMP error message,
|
||||
if (header->protocol == IPPROTO_ICMP) {
|
||||
icmp_header* icmpHeader = (icmp_header*)originalData;
|
||||
if (is_icmp_error(icmpHeader->code))
|
||||
if (header.protocol == IPPROTO_ICMP) {
|
||||
uint8 type;
|
||||
if (gBufferModule->restore_header(buffer, header.HeaderLength(), &type,
|
||||
1) != B_OK || is_icmp_error(type))
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@@ -443,15 +441,20 @@ icmp_error_reply(net_protocol* protocol, net_buffer* buffer, uint32 code,
|
||||
return B_ERROR;
|
||||
|
||||
// a non-initial fragment
|
||||
if ((header->FragmentOffset() & IP_FRAGMENT_OFFSET_MASK) != 0)
|
||||
if ((header.FragmentOffset() & IP_FRAGMENT_OFFSET_MASK) != 0)
|
||||
return B_ERROR;
|
||||
|
||||
net_buffer* reply = gBufferModule->create(256);
|
||||
if (reply == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memcpy(reply->source, buffer->destination, buffer->destination->sa_len);
|
||||
memcpy(reply->destination, buffer->source, buffer->source->sa_len);
|
||||
|
||||
if (buffer->destination->sa_family == AF_INET) {
|
||||
memcpy(reply->source, buffer->destination, buffer->destination->sa_len);
|
||||
memcpy(reply->destination, buffer->source, buffer->source->sa_len);
|
||||
} else {
|
||||
fill_sockaddr_in((sockaddr_in*)reply->source, header.destination);
|
||||
fill_sockaddr_in((sockaddr_in*)reply->destination, header.source);
|
||||
}
|
||||
|
||||
// Now prepare the ICMP header
|
||||
NetBufferPrepend<icmp_header> icmpHeader(reply);
|
||||
@@ -463,14 +466,19 @@ icmp_error_reply(net_protocol* protocol, net_buffer* buffer, uint32 code,
|
||||
*ICMPChecksumField(reply)
|
||||
= gBufferModule->checksum(reply, 0, reply->size, true);
|
||||
|
||||
// Append IP header + 64 bits of the original datagram
|
||||
gBufferModule->append(reply, header, sizeof(ipv4_header));
|
||||
// Append IP header + 8 byte of the original datagram
|
||||
status_t status = gBufferModule->append_restored_header(reply, buffer, 0,
|
||||
std::min(header.HeaderLength() + 8, (int)header.TotalLength()));
|
||||
if (status == B_OK) {
|
||||
net_domain* domain = get_domain(buffer);
|
||||
if (domain == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
net_domain* domain = get_domain(buffer);
|
||||
if (domain == NULL)
|
||||
return B_ERROR;
|
||||
TRACE(" send ICMP message to %s\n", AddressString(
|
||||
domain->address_module, reply->destination, true).Data());
|
||||
|
||||
status = domain->module->send_data(NULL, reply);
|
||||
status = domain->module->send_data(NULL, reply);
|
||||
}
|
||||
if (status != B_OK)
|
||||
gBufferModule->free(reply);
|
||||
|
||||
|
||||
@@ -1378,7 +1378,7 @@ ipv4_send_routed_data(net_protocol* _protocol, struct net_route* route,
|
||||
if (header.Status() != B_OK)
|
||||
return header.Status();
|
||||
|
||||
header->version = IP_VERSION;
|
||||
header->version = IPV4_VERSION;
|
||||
header->header_length = sizeof(ipv4_header) / 4;
|
||||
header->service_type = protocol ? protocol->service_type : 0;
|
||||
header->total_length = htons(buffer->size);
|
||||
@@ -1397,6 +1397,8 @@ ipv4_send_routed_data(net_protocol* _protocol, struct net_route* route,
|
||||
|
||||
header->source = source.sin_addr.s_addr;
|
||||
header->destination = destination.sin_addr.s_addr;
|
||||
|
||||
TRACE_ONLY(dump_ipv4_header(*header));
|
||||
} else {
|
||||
// if IP_HDRINCL, check if the source address is set
|
||||
NetBufferHeaderReader<ipv4_header> header(buffer);
|
||||
@@ -1409,6 +1411,9 @@ ipv4_send_routed_data(net_protocol* _protocol, struct net_route* route,
|
||||
header.Sync();
|
||||
} else
|
||||
checksumNeeded = false;
|
||||
|
||||
TRACE(" Header was already supplied:");
|
||||
TRACE_ONLY(dump_ipv4_header(*header));
|
||||
}
|
||||
|
||||
if (buffer->size > 0xffff)
|
||||
@@ -1444,7 +1449,7 @@ ipv4_send_data(net_protocol* _protocol, net_buffer* buffer)
|
||||
|
||||
TRACE_SK(protocol, "SendData(%p [%ld bytes])", buffer, buffer->size);
|
||||
|
||||
if (protocol && (protocol->flags & IP_FLAG_HEADER_INCLUDED)) {
|
||||
if (protocol != NULL && (protocol->flags & IP_FLAG_HEADER_INCLUDED)) {
|
||||
if (buffer->size < sizeof(ipv4_header))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
@@ -1551,7 +1556,7 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
ipv4_header& header = bufferHeader.Data();
|
||||
TRACE_ONLY(dump_ipv4_header(header));
|
||||
|
||||
if (header.version != IP_VERSION)
|
||||
if (header.version != IPV4_VERSION)
|
||||
return B_BAD_TYPE;
|
||||
|
||||
uint16 packetLength = header.TotalLength();
|
||||
@@ -1585,6 +1590,7 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
buffer->destination, &buffer->interface)) {
|
||||
TRACE(" ReceiveData(): packet was not for us %x -> %x",
|
||||
ntohl(header.source), ntohl(header.destination));
|
||||
|
||||
// Send ICMP error: Host unreachable
|
||||
sDomain->module->error_reply(NULL, buffer,
|
||||
icmp_encode(ICMP_TYPE_UNREACH, ICMP_CODE_HOST_UNREACH), NULL);
|
||||
@@ -1600,7 +1606,6 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
memcpy(buffer->destination, &destination, sizeof(sockaddr_in));
|
||||
|
||||
uint8 protocol = buffer->protocol = header.protocol;
|
||||
buffer->hoplimit = header.time_to_live;
|
||||
|
||||
// remove any trailing/padding data
|
||||
status_t status = gBufferModule->trim(buffer, packetLength);
|
||||
@@ -1625,21 +1630,15 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the ipv4 header for ICMP processing
|
||||
// TODO: solve this differently, and discard net_buffer::network_header!
|
||||
ipv4_header* clonedHeader = (ipv4_header*)malloc(sizeof(ipv4_header));
|
||||
if (clonedHeader == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memcpy(clonedHeader, &header, sizeof(ipv4_header));
|
||||
buffer->network_header = clonedHeader;
|
||||
|
||||
// Since the buffer might have been changed (reassembled fragment)
|
||||
// we must no longer access bufferHeader or header anymore after
|
||||
// this point
|
||||
|
||||
bool rawDelivered = raw_receive_data(buffer);
|
||||
|
||||
// Preserve the ipv4 header for ICMP processing
|
||||
gBufferModule->store_header(buffer);
|
||||
|
||||
gBufferModule->remove_header(buffer, headerLength);
|
||||
// the header is of variable size and may include IP options
|
||||
// (TODO: that we ignore for now)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <ByteOrder.h>
|
||||
|
||||
|
||||
#define IP_VERSION 4
|
||||
#define IPV4_VERSION 4
|
||||
|
||||
// fragment flags
|
||||
#define IP_RESERVED_FLAG 0x8000
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include <debug_paranoia.h>
|
||||
|
||||
#define DATA_NODE_READ_ONLY 0x1
|
||||
#define DATA_NODE_STORED_HEADER 0x2
|
||||
|
||||
struct header_space {
|
||||
uint16 size;
|
||||
@@ -131,6 +132,7 @@ struct net_buffer_private : net_buffer {
|
||||
data_header* allocation_header;
|
||||
// the current place where we allocate header space (nodes, ...)
|
||||
ancillary_data_container* ancillary_data;
|
||||
size_t stored_header_length;
|
||||
|
||||
struct {
|
||||
struct sockaddr_storage source;
|
||||
@@ -575,12 +577,29 @@ private:
|
||||
#endif // NET_BUFFER_TRACING
|
||||
|
||||
|
||||
static void
|
||||
dump_address(const char* prefix, sockaddr* address)
|
||||
{
|
||||
if (address == NULL || address->sa_len == 0)
|
||||
return;
|
||||
|
||||
dprintf(" %s: length %u, family %u\n", prefix, address->sa_len,
|
||||
address->sa_family);
|
||||
|
||||
dump_block((char*)address + 2, address->sa_len - 2, " ");
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_buffer(net_buffer* _buffer)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
|
||||
dprintf("buffer %p, size %ld\n", buffer, buffer->size);
|
||||
dprintf("buffer %p, size %ld, flags %lx\n", buffer, buffer->size,
|
||||
buffer->flags);
|
||||
dump_address("source", buffer->source);
|
||||
dump_address("destination", buffer->destination);
|
||||
|
||||
data_node* node = NULL;
|
||||
while ((node = (data_node*)list_get_next_item(&buffer->buffers, node))
|
||||
!= NULL) {
|
||||
@@ -1038,10 +1057,7 @@ copy_metadata(net_buffer* destination, const net_buffer* source)
|
||||
destination->interface = source->interface;
|
||||
destination->offset = source->offset;
|
||||
destination->protocol = source->protocol;
|
||||
destination->hoplimit = source->hoplimit;
|
||||
destination->type = source->type;
|
||||
|
||||
destination->network_header = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1077,7 +1093,7 @@ create_buffer(size_t headerSpace)
|
||||
list_add_item(&buffer->buffers, node);
|
||||
|
||||
buffer->ancillary_data = NULL;
|
||||
buffer->network_header = NULL;
|
||||
buffer->stored_header_length = 0;
|
||||
|
||||
buffer->source = (sockaddr*)&buffer->storage.source;
|
||||
buffer->destination = (sockaddr*)&buffer->storage.destination;
|
||||
@@ -1120,7 +1136,6 @@ free_buffer(net_buffer* _buffer)
|
||||
}
|
||||
|
||||
delete_ancillary_data_container(buffer->ancillary_data);
|
||||
free(buffer->network_header);
|
||||
|
||||
release_data_header(buffer->allocation_header);
|
||||
|
||||
@@ -1534,6 +1549,13 @@ prepend_size(net_buffer* _buffer, size_t size, void** _contiguousBuffer)
|
||||
find_thread(NULL), buffer, size, node->HeaderSpace()));
|
||||
//dump_buffer(buffer);
|
||||
|
||||
if ((node->flags & DATA_NODE_STORED_HEADER) != 0) {
|
||||
// throw any stored headers away
|
||||
node->AddHeaderSpace(buffer->stored_header_length);
|
||||
node->flags &= ~DATA_NODE_STORED_HEADER;
|
||||
buffer->stored_header_length = 0;
|
||||
}
|
||||
|
||||
if (node->HeaderSpace() < size) {
|
||||
// we need to prepend new buffers
|
||||
|
||||
@@ -1791,6 +1813,7 @@ remove_header(net_buffer* _buffer, size_t bytes)
|
||||
left -= node->used;
|
||||
remove_data_node(node);
|
||||
node = NULL;
|
||||
buffer->stored_header_length = 0;
|
||||
}
|
||||
|
||||
// cut remaining node, if any
|
||||
@@ -1799,7 +1822,10 @@ remove_header(net_buffer* _buffer, size_t bytes)
|
||||
size_t cut = min_c(node->used, left);
|
||||
node->offset = 0;
|
||||
node->start += cut;
|
||||
node->AddHeaderSpace(cut);
|
||||
if ((node->flags & DATA_NODE_STORED_HEADER) != 0)
|
||||
buffer->stored_header_length += cut;
|
||||
else
|
||||
node->AddHeaderSpace(cut);
|
||||
node->used -= cut;
|
||||
|
||||
node = (data_node*)list_get_next_item(&buffer->buffers, node);
|
||||
@@ -2005,6 +2031,107 @@ transfer_ancillary_data(net_buffer* _from, net_buffer* _to)
|
||||
}
|
||||
|
||||
|
||||
/*! Stores the current header position; even if the header is removed with
|
||||
remove_header(), you can still reclaim it later using restore_header(),
|
||||
unless you prepended different data (in which case restoring will fail).
|
||||
*/
|
||||
status_t
|
||||
store_header(net_buffer* _buffer)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
data_node* node = (data_node*)list_get_first_item(&buffer->buffers);
|
||||
if (node == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
if ((node->flags & DATA_NODE_STORED_HEADER) != 0) {
|
||||
// Someone else already stored the header - since we cannot
|
||||
// differentiate between them, we throw away everything
|
||||
node->AddHeaderSpace(buffer->stored_header_length);
|
||||
node->flags &= ~DATA_NODE_STORED_HEADER;
|
||||
buffer->stored_header_length = 0;
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
buffer->stored_header_length = 0;
|
||||
node->flags |= DATA_NODE_STORED_HEADER;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
ssize_t
|
||||
stored_header_length(net_buffer* _buffer)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
data_node* node = (data_node*)list_get_first_item(&buffer->buffers);
|
||||
if (node == NULL || (node->flags & DATA_NODE_STORED_HEADER) == 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return buffer->stored_header_length;
|
||||
}
|
||||
|
||||
|
||||
/*! Reads from the complete buffer with an eventually stored header.
|
||||
This function does not care whether or not there is a stored header at
|
||||
all - you have to use the stored_header_length() function to find out.
|
||||
*/
|
||||
status_t
|
||||
restore_header(net_buffer* _buffer, uint32 offset, void* data, size_t bytes)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
data_node* node = (data_node*)list_get_first_item(&buffer->buffers);
|
||||
if (node == NULL
|
||||
|| offset + bytes > buffer->stored_header_length + buffer->size)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// We have the data, so copy it out
|
||||
|
||||
memcpy(data, node->start - buffer->stored_header_length,
|
||||
std::min(bytes, buffer->stored_header_length));
|
||||
|
||||
if (bytes <= buffer->stored_header_length)
|
||||
return B_OK;
|
||||
|
||||
data = (uint8*)data + buffer->stored_header_length;
|
||||
bytes -= buffer->stored_header_length;
|
||||
|
||||
return read_data(_buffer, 0, data, bytes);
|
||||
}
|
||||
|
||||
|
||||
/*! Copies from the complete \a source buffer with an eventually stored header
|
||||
to the specified target \a buffer.
|
||||
This function does not care whether or not there is a stored header at
|
||||
all - you have to use the stored_header_length() function to find out.
|
||||
*/
|
||||
status_t
|
||||
append_restored_header(net_buffer* buffer, net_buffer* _source, uint32 offset,
|
||||
size_t bytes)
|
||||
{
|
||||
net_buffer_private* source = (net_buffer_private*)_source;
|
||||
data_node* node = (data_node*)list_get_first_item(&source->buffers);
|
||||
if (node == NULL
|
||||
|| offset + bytes > source->stored_header_length + source->size)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// We have the data, so copy it out
|
||||
|
||||
status_t status = append_data(buffer,
|
||||
node->start - source->stored_header_length,
|
||||
std::min(bytes, source->stored_header_length));
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
if (bytes <= source->stored_header_length)
|
||||
return B_OK;
|
||||
|
||||
bytes -= source->stored_header_length;
|
||||
|
||||
return append_cloned_data(buffer, source, 0, bytes);
|
||||
}
|
||||
|
||||
|
||||
/*! 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.
|
||||
@@ -2217,6 +2344,11 @@ net_buffer_module_info gNetBufferModule = {
|
||||
get_ancillary_data,
|
||||
transfer_ancillary_data,
|
||||
|
||||
store_header,
|
||||
stored_header_length,
|
||||
restore_header,
|
||||
append_restored_header,
|
||||
|
||||
direct_access,
|
||||
read_data,
|
||||
write_data,
|
||||
|
||||
Reference in New Issue
Block a user