Author: axeld Date: 2010-07-22 14:26:46 +0200 (Thu, 22 Jul 2010) New Revision: 37688 Changeset: http://dev.haiku-os.org/changeset/37688 Modified: haiku/trunk/headers/private/net/net_buffer.h haiku/trunk/src/add-ons/kernel/network/protocols/icmp/icmp.cpp haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.h haiku/trunk/src/add-ons/kernel/network/stack/net_buffer.cpp Log: * 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. Modified: haiku/trunk/headers/private/net/net_buffer.h =================================================================== --- haiku/trunk/headers/private/net/net_buffer.h 2010-07-22 12:20:45 UTC (rev 37687) +++ haiku/trunk/headers/private/net/net_buffer.h 2010-07-22 12:26:46 UTC (rev 37688) @@ -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 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); }; Modified: haiku/trunk/src/add-ons/kernel/network/protocols/icmp/icmp.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/icmp/icmp.cpp 2010-07-22 12:20:45 UTC (rev 37687) +++ haiku/trunk/src/add-ons/kernel/network/protocols/icmp/icmp.cpp 2010-07-22 12:26:46 UTC (rev 37688) @@ -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 @@ } +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_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 @@ 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 @@ *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); Modified: haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp 2010-07-22 12:20:45 UTC (rev 37687) +++ haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp 2010-07-22 12:26:46 UTC (rev 37688) @@ -1378,7 +1378,7 @@ 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 @@ 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 @@ header.Sync(); } else checksumNeeded = false; + + TRACE(" Header was already supplied:"); + TRACE_ONLY(dump_ipv4_header(*header)); } if (buffer->size > 0xffff) @@ -1444,7 +1449,7 @@ 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_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 @@ 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 @@ 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 @@ } } - // 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) Modified: haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.h =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.h 2010-07-22 12:20:45 UTC (rev 37687) +++ haiku/trunk/src/add-ons/kernel/network/protocols/ipv4/ipv4.h 2010-07-22 12:26:46 UTC (rev 37688) @@ -11,7 +11,7 @@ #include <ByteOrder.h> -#define IP_VERSION 4 +#define IPV4_VERSION 4 // fragment flags #define IP_RESERVED_FLAG 0x8000 Modified: haiku/trunk/src/add-ons/kernel/network/stack/net_buffer.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/stack/net_buffer.cpp 2010-07-22 12:20:45 UTC (rev 37687) +++ haiku/trunk/src/add-ons/kernel/network/stack/net_buffer.cpp 2010-07-22 12:26:46 UTC (rev 37688) @@ -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 @@ 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; @@ -576,11 +578,28 @@ 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 @@ 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 @@ 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 @@ } delete_ancillary_data_container(buffer->ancillary_data); - free(buffer->network_header); release_data_header(buffer->allocation_header); @@ -1534,6 +1549,13 @@ 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 @@ left -= node->used; remove_data_node(node); node = NULL; + buffer->stored_header_length = 0; } // cut remaining node, if any @@ -1799,7 +1822,10 @@ 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 @@ } +/*! 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 @@ get_ancillary_data, transfer_ancillary_data, + store_header, + stored_header_length, + restore_header, + append_restored_header, + direct_access, read_data, write_data,