* 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:
Axel Dörfler
2010-07-22 12:26:46 +00:00
parent e82b1340a7
commit 74a4c33fda
5 changed files with 254 additions and 112 deletions
+58 -55
View File
@@ -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
+139 -7
View File
@@ -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,