* First part of ICMP support: this is based on the work by Ivo Vachkov (GSoC
2007), and Yin Qiu (GSoC 2008). And even though I needed to rewrite pretty much all of it because of the countless bugs and problems it had, it still shares the same architectural problems of introducing a domain dependent error mechanism to the upper layers, and needing the net_buffer::network_header hack. This I will rework later. * net_buffer's append_size(), and prepend_size() will now gracefully handle buffers without a data node. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37647 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef ICMP_H
|
||||
#define ICMP_H
|
||||
|
||||
|
||||
// TODO: this will go private soon
|
||||
|
||||
|
||||
// RFC 792
|
||||
#define ICMP_TYPE_ECHO_REPLY 0
|
||||
#define ICMP_TYPE_UNREACH 3
|
||||
#define ICMP_TYPE_SOURCE_QUENCH 4
|
||||
#define ICMP_TYPE_REDIRECT 5
|
||||
#define ICMP_TYPE_ECHO_REQUEST 8
|
||||
#define ICMP_TYPE_TIME_EXCEEDED 11
|
||||
#define ICMP_TYPE_PARAM_PROBLEM 12
|
||||
#define ICMP_TYPE_TIMESTAMP_REQUEST 13
|
||||
#define ICMP_TYPE_TIMESTAMP_REPLY 14
|
||||
#define ICMP_TYPE_INFO_REQUEST 15
|
||||
#define ICMP_TYPE_INFO_REPLY 16
|
||||
// RFC 950
|
||||
#define ICMP_TYPE_ADDR_MASK_REQUEST 17
|
||||
#define ICMP_TYPE_ADDR_MASK_REPLY 18
|
||||
|
||||
#define ICMP_CODE_TIMEEX_IN_TRANSIT 0
|
||||
#define ICMP_CODE_TIMEEX_FRAG 1
|
||||
#define ICMP_CODE_PARAM_PROBLEM 0
|
||||
#define ICMP_CODE_SOURCE_QUENCH 0
|
||||
#define ICMP_CODE_NET_UNREACH 0
|
||||
#define ICMP_CODE_HOST_UNREACH 1
|
||||
#define ICMP_CODE_PROTO_UNREACH 2
|
||||
#define ICMP_CODE_PORT_UNREACH 3
|
||||
#define ICMP_CODE_FRAG_NEEDED 4
|
||||
#define ICMP_CODE_SOURCE_ROUTE_FAIL 5
|
||||
#define ICMP_CODE_REDIRECT_NET 0
|
||||
#define ICMP_CODE_REDIRECT_HOST 1
|
||||
#define ICMP_CODE_REDIRECT_TOS_NET 2
|
||||
#define ICMP_CODE_REDIRECT_TOS_HOST 3
|
||||
|
||||
|
||||
static inline void
|
||||
icmp_decode(uint32 errorCode, uint8 &type, uint8 &code)
|
||||
{
|
||||
type = errorCode >> 8;
|
||||
code = errorCode & 0x0FF;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32
|
||||
icmp_encode(uint8 type, uint8 code)
|
||||
{
|
||||
return ((uint32)type) << 8 + code;
|
||||
}
|
||||
|
||||
|
||||
#endif // ICMP_H
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
|
||||
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef NET_BUFFER_H
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#define NET_BUFFER_MODULE_NAME "network/stack/buffer/v1"
|
||||
|
||||
|
||||
typedef struct net_buffer {
|
||||
struct list_link link;
|
||||
|
||||
@@ -36,7 +37,10 @@ typedef struct net_buffer {
|
||||
uint32 flags;
|
||||
uint32 size;
|
||||
uint8 protocol;
|
||||
|
||||
// TODO: these two should go away again
|
||||
uint8 hoplimit;
|
||||
void * network_header;
|
||||
} net_buffer;
|
||||
|
||||
struct ancillary_data_container;
|
||||
@@ -98,4 +102,5 @@ struct net_buffer_module_info {
|
||||
void (*dump)(net_buffer *buffer);
|
||||
};
|
||||
|
||||
|
||||
#endif // NET_BUFFER_H
|
||||
|
||||
@@ -70,7 +70,7 @@ struct net_protocol_module_info {
|
||||
status_t (*receive_data)(net_buffer *data);
|
||||
status_t (*deliver_data)(net_protocol *protocol, net_buffer *data);
|
||||
|
||||
status_t (*error)(uint32 code, net_buffer *data);
|
||||
status_t (*error_received)(uint32 code, net_buffer *data);
|
||||
status_t (*error_reply)(net_protocol *self, net_buffer *causedError,
|
||||
uint32 code, void *errorData);
|
||||
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
SubDir HAIKU_TOP src add-ons kernel network protocols icmp ;
|
||||
|
||||
SetSubDirSupportedPlatformsBeOSCompatible ;
|
||||
|
||||
if $(TARGET_PLATFORM) != haiku {
|
||||
UseHeaders [ FStandardOSHeaders ] : true ;
|
||||
# Needed for <support/Errors.h> and maybe other stuff.
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) headers posix ] : true ;
|
||||
# We need the public network headers also when not compiling for Haiku.
|
||||
# Unfortunately we get more than we want, namely all POSIX headers.
|
||||
}
|
||||
|
||||
UseHeaders [ FDirName $(HAIKU_TOP) src add-ons kernel network protocols ipv4 ] ;
|
||||
UsePrivateHeaders kernel net ;
|
||||
|
||||
KernelAddon icmp :
|
||||
icmp.cpp
|
||||
;
|
||||
|
||||
# Installation
|
||||
HaikuInstall install-networking : /boot/home/config/add-ons/kernel/haiku_network/protocols
|
||||
: icmp ;
|
||||
|
||||
Package haiku-networkingkit-cvs :
|
||||
haiku :
|
||||
boot home config add-ons kernel haiku_network protocols ;
|
||||
|
||||
@@ -7,24 +7,36 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <net_datalink.h>
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
#include <NetBufferUtilities.h>
|
||||
/*! RFC 792 details the ICMP protocol, RFC 1122 lists when an ICMP error must,
|
||||
shall, or must not be sent.
|
||||
*/
|
||||
|
||||
#include <KernelExport.h>
|
||||
#include <util/list.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <netinet/in.h>
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
//#define TRACE_ICMP
|
||||
#include <KernelExport.h>
|
||||
#include <OS.h>
|
||||
|
||||
#include <icmp.h>
|
||||
#include <net_datalink.h>
|
||||
#include <net_protocol.h>
|
||||
#include <net_stack.h>
|
||||
#include <NetBufferUtilities.h>
|
||||
|
||||
//#include <util/list.h>
|
||||
|
||||
#include "ipv4.h"
|
||||
|
||||
|
||||
#define TRACE_ICMP
|
||||
#ifdef TRACE_ICMP
|
||||
# define TRACE(x) dprintf x
|
||||
# define TRACE(x...) dprintf(x)
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
# define TRACE(x...) ;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -44,6 +56,12 @@ struct icmp_header {
|
||||
uint16 _reserved;
|
||||
uint16 next_mtu;
|
||||
} path_mtu;
|
||||
uint32 gateway;
|
||||
struct {
|
||||
uint16 unused;
|
||||
uint16 mtu;
|
||||
} frag;
|
||||
|
||||
uint32 zero;
|
||||
};
|
||||
};
|
||||
@@ -70,6 +88,36 @@ net_buffer_module_info* gBufferModule;
|
||||
static net_stack_module_info* sStackModule;
|
||||
|
||||
|
||||
static net_domain*
|
||||
get_domain(struct net_buffer* buffer)
|
||||
{
|
||||
net_domain* domain;
|
||||
if (buffer->interface != NULL)
|
||||
domain = buffer->interface->domain;
|
||||
else
|
||||
domain = sStackModule->get_domain(buffer->source->sa_family);
|
||||
|
||||
if (domain == NULL || domain->module == NULL)
|
||||
return NULL;
|
||||
|
||||
return domain;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
is_icmp_error(uint8 type)
|
||||
{
|
||||
return type == ICMP_TYPE_UNREACH
|
||||
|| type == ICMP_TYPE_PARAM_PROBLEM
|
||||
|| type == ICMP_TYPE_REDIRECT
|
||||
|| type == ICMP_TYPE_TIME_EXCEEDED
|
||||
|| type == ICMP_TYPE_SOURCE_QUENCH;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - module API
|
||||
|
||||
|
||||
net_protocol*
|
||||
icmp_init_protocol(net_socket* socket)
|
||||
{
|
||||
@@ -234,42 +282,49 @@ icmp_get_mtu(net_protocol* protocol, const struct sockaddr* address)
|
||||
status_t
|
||||
icmp_receive_data(net_buffer* buffer)
|
||||
{
|
||||
TRACE(("ICMP received some data, buffer length %lu\n", buffer->size));
|
||||
TRACE("ICMP received some data, buffer length %lu\n", buffer->size);
|
||||
|
||||
net_domain* domain;
|
||||
if (buffer->interface != NULL)
|
||||
domain = buffer->interface->domain;
|
||||
else
|
||||
domain = sStackModule->get_domain(buffer->source->sa_family);
|
||||
|
||||
if (domain == NULL || domain->module == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
NetBufferHeaderReader<icmp_header> bufferHeader(buffer);
|
||||
if (bufferHeader.Status() < B_OK)
|
||||
return bufferHeader.Status();
|
||||
|
||||
icmp_header &header = bufferHeader.Data();
|
||||
icmp_header& header = bufferHeader.Data();
|
||||
uint8 type = header.type;
|
||||
|
||||
TRACE((" got type %u, code %u, checksum %u\n", header.type, header.code,
|
||||
ntohs(header.checksum)));
|
||||
TRACE((" computed checksum: %ld\n",
|
||||
gBufferModule->checksum(buffer, 0, buffer->size, true)));
|
||||
TRACE(" got type %u, code %u, checksum %u\n", header.type, header.code,
|
||||
ntohs(header.checksum));
|
||||
TRACE(" computed checksum: %ld\n",
|
||||
gBufferModule->checksum(buffer, 0, buffer->size, true));
|
||||
|
||||
if (gBufferModule->checksum(buffer, 0, buffer->size, true) != 0)
|
||||
return B_BAD_DATA;
|
||||
|
||||
switch (header.type) {
|
||||
switch (type) {
|
||||
case ICMP_TYPE_ECHO_REPLY:
|
||||
break;
|
||||
|
||||
case ICMP_TYPE_ECHO_REQUEST:
|
||||
{
|
||||
net_domain* domain;
|
||||
if (buffer->interface != NULL) {
|
||||
domain = buffer->interface->domain;
|
||||
net_domain* domain = get_domain(buffer);
|
||||
if (domain == NULL)
|
||||
break;
|
||||
|
||||
if (buffer->interface != NULL) {
|
||||
// We only reply to echo requests of our local interface; we
|
||||
// don't reply to broadcast requests
|
||||
if (!domain->address_module->equal_addresses(
|
||||
buffer->interface->address, buffer->destination))
|
||||
break;
|
||||
} else
|
||||
domain = sStackModule->get_domain(buffer->source->sa_family);
|
||||
|
||||
if (domain == NULL || domain->module == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
net_buffer* reply = gBufferModule->duplicate(buffer);
|
||||
if (reply == NULL)
|
||||
@@ -278,13 +333,13 @@ icmp_receive_data(net_buffer* buffer)
|
||||
gBufferModule->swap_addresses(reply);
|
||||
|
||||
// There already is an ICMP header, and we'll reuse it
|
||||
NetBufferHeaderReader<icmp_header> header(reply);
|
||||
NetBufferHeaderReader<icmp_header> newHeader(reply);
|
||||
|
||||
header->type = ICMP_TYPE_ECHO_REPLY;
|
||||
header->code = 0;
|
||||
header->checksum = 0;
|
||||
newHeader->type = type == ICMP_TYPE_ECHO_REPLY;
|
||||
newHeader->code = 0;
|
||||
newHeader->checksum = 0;
|
||||
|
||||
header.Sync();
|
||||
newHeader.Sync();
|
||||
|
||||
*ICMPChecksumField(reply) = gBufferModule->checksum(reply, 0,
|
||||
reply->size, true);
|
||||
@@ -294,9 +349,36 @@ icmp_receive_data(net_buffer* buffer)
|
||||
gBufferModule->free(reply);
|
||||
return status;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case ICMP_TYPE_UNREACH:
|
||||
case ICMP_TYPE_SOURCE_QUENCH:
|
||||
case ICMP_TYPE_PARAM_PROBLEM:
|
||||
case ICMP_TYPE_TIME_EXCEEDED:
|
||||
{
|
||||
net_domain* domain = get_domain(buffer);
|
||||
if (domain == NULL)
|
||||
break;
|
||||
|
||||
uint32 error = icmp_encode(header.type, header.code);
|
||||
if (error > 0) {
|
||||
// Deliver the error to the domain protocol which will
|
||||
// propagate the error to the upper protocols
|
||||
return domain->module->error_received(error, buffer);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ICMP_TYPE_REDIRECT:
|
||||
// TODO: Update the routing table
|
||||
case ICMP_TYPE_TIMESTAMP_REQUEST:
|
||||
case ICMP_TYPE_TIMESTAMP_REPLY:
|
||||
case ICMP_TYPE_INFO_REQUEST:
|
||||
case ICMP_TYPE_INFO_REPLY:
|
||||
default:
|
||||
// RFC 1122 3.2.2:
|
||||
// Unknown ICMP messages are silently discarded
|
||||
dprintf("ICMP: received unhandled type %u, code %u\n", header.type,
|
||||
header.code);
|
||||
break;
|
||||
@@ -308,17 +390,96 @@ icmp_receive_data(net_buffer* buffer)
|
||||
|
||||
|
||||
status_t
|
||||
icmp_error(uint32 code, net_buffer* data)
|
||||
icmp_error_received(uint32 code, net_buffer* data)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/*! Sends an ICMP error message to the source of the \a buffer causing the
|
||||
error.
|
||||
*/
|
||||
status_t
|
||||
icmp_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code,
|
||||
icmp_error_reply(net_protocol* protocol, net_buffer* buffer, uint32 code,
|
||||
void* errorData)
|
||||
{
|
||||
return B_ERROR;
|
||||
TRACE("icmp_error_reply(code %#" B_PRIx32 ")\n", code);
|
||||
|
||||
uint8 icmpType, icmpCode;
|
||||
icmp_decode(code, icmpType, icmpCode);
|
||||
|
||||
NetBufferHeaderReader<ipv4_header> bufferHeader(buffer);
|
||||
size_t offset = 0;
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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))
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// a datagram to an IP multicast or broadcast address,
|
||||
if ((buffer->flags & (MSG_BCAST | MSG_MCAST)) != 0)
|
||||
return B_ERROR;
|
||||
|
||||
// a non-initial fragment
|
||||
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);
|
||||
|
||||
// Now prepare the ICMP header
|
||||
NetBufferPrepend<icmp_header> icmpHeader(reply);
|
||||
icmpHeader->type = icmpType;
|
||||
icmpHeader->code = icmpCode;
|
||||
icmpHeader->gateway = errorData ? *((uint32*)errorData) : 0;
|
||||
icmpHeader->checksum = 0;
|
||||
icmpHeader.Sync();
|
||||
*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));
|
||||
|
||||
net_domain* domain = get_domain(buffer);
|
||||
if (domain == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
status = domain->module->send_data(NULL, reply);
|
||||
if (status != B_OK)
|
||||
gBufferModule->free(reply);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -382,7 +543,7 @@ net_protocol_module_info sICMPModule = {
|
||||
icmp_get_mtu,
|
||||
icmp_receive_data,
|
||||
NULL, // deliver_data()
|
||||
icmp_error,
|
||||
icmp_error_received,
|
||||
icmp_error_reply,
|
||||
NULL, // add_ancillary_data()
|
||||
NULL, // process_ancillary_data()
|
||||
|
||||
@@ -7,9 +7,11 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "ipv4.h"
|
||||
#include "ipv4_address.h"
|
||||
#include "multicast.h"
|
||||
|
||||
#include <icmp.h>
|
||||
#include <net_datalink.h>
|
||||
#include <net_datalink_protocol.h>
|
||||
#include <net_device.h>
|
||||
@@ -18,7 +20,6 @@
|
||||
#include <NetBufferUtilities.h>
|
||||
#include <ProtocolUtilities.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
#include <KernelExport.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <util/list.h>
|
||||
@@ -48,42 +49,12 @@
|
||||
#endif
|
||||
|
||||
|
||||
struct ipv4_header {
|
||||
#if B_HOST_IS_LENDIAN == 1
|
||||
uint8 header_length : 4; // header length in 32-bit words
|
||||
uint8 version : 4;
|
||||
#else
|
||||
uint8 version : 4;
|
||||
uint8 header_length : 4;
|
||||
#endif
|
||||
uint8 service_type;
|
||||
uint16 total_length;
|
||||
uint16 id;
|
||||
uint16 fragment_offset;
|
||||
uint8 time_to_live;
|
||||
uint8 protocol;
|
||||
uint16 checksum;
|
||||
in_addr_t source;
|
||||
in_addr_t destination;
|
||||
|
||||
uint16 HeaderLength() const { return header_length << 2; }
|
||||
uint16 TotalLength() const { return ntohs(total_length); }
|
||||
uint16 FragmentOffset() const { return ntohs(fragment_offset); }
|
||||
} _PACKED;
|
||||
|
||||
#define IP_VERSION 4
|
||||
|
||||
// fragment flags
|
||||
#define IP_RESERVED_FLAG 0x8000
|
||||
#define IP_DONT_FRAGMENT 0x4000
|
||||
#define IP_MORE_FRAGMENTS 0x2000
|
||||
#define IP_FRAGMENT_OFFSET_MASK 0x1fff
|
||||
|
||||
#define MAX_HASH_FRAGMENTS 64
|
||||
// slots in the fragment packet's hash
|
||||
#define FRAGMENT_TIMEOUT 60000000LL
|
||||
// discard fragment after 60 seconds
|
||||
|
||||
|
||||
typedef DoublyLinkedList<struct net_buffer,
|
||||
DoublyLinkedListCLink<struct net_buffer> > FragmentList;
|
||||
|
||||
@@ -116,7 +87,7 @@ public:
|
||||
static void StaleTimer(struct net_timer* timer, void* data);
|
||||
|
||||
private:
|
||||
FragmentPacket *fNext;
|
||||
FragmentPacket* fNext;
|
||||
struct ipv4_packet_key fKey;
|
||||
bool fReceivedLastFragment;
|
||||
int32 fBytesLeft;
|
||||
@@ -232,7 +203,7 @@ FragmentPacket::FragmentPacket(const ipv4_packet_key &key)
|
||||
fReceivedLastFragment(false),
|
||||
fBytesLeft(IP_MAXPACKET)
|
||||
{
|
||||
gStackModule->init_timer(&fTimer, StaleTimer, this);
|
||||
gStackModule->init_timer(&fTimer, FragmentPacket::StaleTimer, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -450,8 +421,15 @@ FragmentPacket::StaleTimer(struct net_timer* timer, void* data)
|
||||
TRACE("Assembling FragmentPacket %p timed out!", packet);
|
||||
|
||||
MutexLocker locker(&sFragmentLock);
|
||||
|
||||
hash_remove(sFragmentHash, packet);
|
||||
locker.Unlock();
|
||||
|
||||
if (!packet->fFragments.IsEmpty()) {
|
||||
// Send error: fragment reassembly time exceeded
|
||||
sDomain->module->error_reply(NULL, packet->fFragments.First(),
|
||||
icmp_encode(ICMP_TYPE_TIME_EXCEEDED, ICMP_CODE_TIMEEX_FRAG), NULL);
|
||||
}
|
||||
|
||||
delete packet;
|
||||
}
|
||||
|
||||
@@ -706,14 +684,14 @@ deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
|
||||
// as Multicast filters are installed with an IPv4 protocol
|
||||
// reference, we need to go and find the appropriate instance
|
||||
// related to the 'receiving protocol' with module 'module'.
|
||||
net_protocol* proto
|
||||
net_protocol* protocol
|
||||
= state->Parent()->Socket()->socket->first_protocol;
|
||||
|
||||
while (proto && proto->module != module)
|
||||
proto = proto->next;
|
||||
while (protocol != NULL && protocol->module != module)
|
||||
protocol = protocol->next;
|
||||
|
||||
if (proto)
|
||||
module->deliver_data(proto, buffer);
|
||||
if (protocol != NULL)
|
||||
module->deliver_data(protocol, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,13 +699,16 @@ deliver_multicast(net_protocol_module_info* module, net_buffer* buffer,
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
/*! Delivers the buffer to all listening raw sockets.
|
||||
Returns \c true if there was any receiver, \c false if not.
|
||||
*/
|
||||
static bool
|
||||
raw_receive_data(net_buffer* buffer)
|
||||
{
|
||||
MutexLocker locker(sRawSocketsLock);
|
||||
|
||||
if (sRawSockets.IsEmpty())
|
||||
return;
|
||||
return false;
|
||||
|
||||
TRACE("RawReceiveData(%i)", buffer->protocol);
|
||||
|
||||
@@ -748,6 +729,8 @@ raw_receive_data(net_buffer* buffer)
|
||||
raw->SocketEnqueue(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1547,7 +1530,7 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
if (bufferHeader.Status() != B_OK)
|
||||
return bufferHeader.Status();
|
||||
|
||||
ipv4_header &header = bufferHeader.Data();
|
||||
ipv4_header& header = bufferHeader.Data();
|
||||
//dump_ipv4_header(header);
|
||||
|
||||
if (header.version != IP_VERSION)
|
||||
@@ -1563,7 +1546,8 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
if (gBufferModule->checksum(buffer, 0, headerLength, true) != 0)
|
||||
return B_BAD_DATA;
|
||||
|
||||
// lower layers notion of Broadcast or Multicast have no relevance to us
|
||||
// lower layers notion of broadcast or multicast have no relevance to us
|
||||
// TODO: they actually have when deciding whether to send an ICMP error
|
||||
buffer->flags &= ~(MSG_BCAST | MSG_MCAST);
|
||||
|
||||
sockaddr_in destination;
|
||||
@@ -1583,6 +1567,9 @@ 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);
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
@@ -1603,7 +1590,7 @@ ipv4_receive_data(net_buffer* buffer)
|
||||
return status;
|
||||
|
||||
// check for fragmentation
|
||||
uint16 fragmentOffset = ntohs(header.fragment_offset);
|
||||
uint16 fragmentOffset = header.FragmentOffset();
|
||||
if ((fragmentOffset & IP_MORE_FRAGMENTS) != 0
|
||||
|| (fragmentOffset & IP_FRAGMENT_OFFSET_MASK) != 0) {
|
||||
// this is a fragment
|
||||
@@ -1620,19 +1607,32 @@ 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
|
||||
|
||||
raw_receive_data(buffer);
|
||||
bool rawDelivered = raw_receive_data(buffer);
|
||||
|
||||
gBufferModule->remove_header(buffer, headerLength);
|
||||
// the header is of variable size and may include IP options
|
||||
// (that we ignore for now)
|
||||
// (TODO: that we ignore for now)
|
||||
|
||||
net_protocol_module_info* module = receiving_protocol(protocol);
|
||||
if (module == NULL) {
|
||||
// no handler for this packet
|
||||
if (!rawDelivered) {
|
||||
sDomain->module->error_reply(NULL, buffer,
|
||||
icmp_encode(ICMP_TYPE_UNREACH, ICMP_CODE_PROTO_UNREACH), NULL);
|
||||
}
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
@@ -1661,9 +1661,16 @@ ipv4_deliver_data(net_protocol* _protocol, net_buffer* buffer)
|
||||
|
||||
|
||||
status_t
|
||||
ipv4_error(uint32 code, net_buffer* data)
|
||||
ipv4_error_received(uint32 code, net_buffer* data)
|
||||
{
|
||||
return B_ERROR;
|
||||
// Extracts the IP header in the ICMP message
|
||||
NetBufferFieldReader<ipv4_header, 8> header(data);
|
||||
net_protocol_module_info* protocol = receiving_protocol(header->protocol);
|
||||
if (protocol == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
// propagate error
|
||||
return protocol->error_received(code, data);
|
||||
}
|
||||
|
||||
|
||||
@@ -1671,7 +1678,12 @@ status_t
|
||||
ipv4_error_reply(net_protocol* protocol, net_buffer* causedError, uint32 code,
|
||||
void* errorData)
|
||||
{
|
||||
return B_ERROR;
|
||||
// Directly obtain the ICMP protocol module
|
||||
net_protocol_module_info* icmp = receiving_protocol(IPPROTO_ICMP);
|
||||
if (icmp == NULL)
|
||||
return B_ERROR;
|
||||
|
||||
return icmp->error_reply(protocol, causedError, code, errorData);
|
||||
}
|
||||
|
||||
|
||||
@@ -1838,7 +1850,7 @@ net_protocol_module_info gIPv4Module = {
|
||||
ipv4_get_mtu,
|
||||
ipv4_receive_data,
|
||||
ipv4_deliver_data,
|
||||
ipv4_error,
|
||||
ipv4_error_received,
|
||||
ipv4_error_reply,
|
||||
NULL, // add_ancillary_data()
|
||||
NULL, // process_ancillary_data()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef IPV4_H
|
||||
#define IPV4_H
|
||||
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <ByteOrder.h>
|
||||
|
||||
|
||||
#define IP_VERSION 4
|
||||
|
||||
// fragment flags
|
||||
#define IP_RESERVED_FLAG 0x8000
|
||||
#define IP_DONT_FRAGMENT 0x4000
|
||||
#define IP_MORE_FRAGMENTS 0x2000
|
||||
#define IP_FRAGMENT_OFFSET_MASK 0x1fff
|
||||
|
||||
|
||||
struct ipv4_header {
|
||||
#if B_HOST_IS_LENDIAN == 1
|
||||
uint8 header_length : 4; // header length in 32-bit words
|
||||
uint8 version : 4;
|
||||
#else
|
||||
uint8 version : 4;
|
||||
uint8 header_length : 4;
|
||||
#endif
|
||||
uint8 service_type;
|
||||
uint16 total_length;
|
||||
uint16 id;
|
||||
uint16 fragment_offset;
|
||||
uint8 time_to_live;
|
||||
uint8 protocol;
|
||||
uint16 checksum;
|
||||
in_addr_t source;
|
||||
in_addr_t destination;
|
||||
|
||||
uint16 HeaderLength() const { return header_length << 2; }
|
||||
uint16 TotalLength() const { return ntohs(total_length); }
|
||||
uint16 FragmentOffset() const { return ntohs(fragment_offset); }
|
||||
} _PACKED;
|
||||
|
||||
|
||||
#endif // IPV4_H
|
||||
@@ -1040,6 +1040,8 @@ copy_metadata(net_buffer* destination, const net_buffer* source)
|
||||
destination->protocol = source->protocol;
|
||||
destination->hoplimit = source->hoplimit;
|
||||
destination->type = source->type;
|
||||
|
||||
destination->network_header = NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1075,6 +1077,7 @@ create_buffer(size_t headerSpace)
|
||||
list_add_item(&buffer->buffers, node);
|
||||
|
||||
buffer->ancillary_data = NULL;
|
||||
buffer->network_header = NULL;
|
||||
|
||||
buffer->source = (sockaddr*)&buffer->storage.source;
|
||||
buffer->destination = (sockaddr*)&buffer->storage.destination;
|
||||
@@ -1117,6 +1120,7 @@ free_buffer(net_buffer* _buffer)
|
||||
}
|
||||
|
||||
delete_ancillary_data_container(buffer->ancillary_data);
|
||||
free(buffer->network_header);
|
||||
|
||||
release_data_header(buffer->allocation_header);
|
||||
|
||||
@@ -1516,6 +1520,11 @@ prepend_size(net_buffer* _buffer, size_t size, void** _contiguousBuffer)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
data_node* node = (data_node*)list_get_first_item(&buffer->buffers);
|
||||
if (node == NULL) {
|
||||
node = add_first_data_node(buffer->allocation_header);
|
||||
if (node == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
T(PrependSize(buffer, size));
|
||||
|
||||
@@ -1628,6 +1637,11 @@ append_size(net_buffer* _buffer, size_t size, void** _contiguousBuffer)
|
||||
{
|
||||
net_buffer_private* buffer = (net_buffer_private*)_buffer;
|
||||
data_node* node = (data_node*)list_get_last_item(&buffer->buffers);
|
||||
if (node == NULL) {
|
||||
node = add_first_data_node(buffer->allocation_header);
|
||||
if (node == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
T(AppendSize(buffer, size));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user