* First (untested) steps into ICMP support for UDP: we should send port

unreached ICMP messages now, and at least signal an error to select() (there
  is no mechanism yet to actually forward the error to userland).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37651 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2010-07-21 14:57:28 +00:00
parent fcea1b1665
commit 2bb43d8246
+193 -71
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2009, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -13,6 +13,8 @@
#include <net_protocol.h>
#include <net_stack.h>
#include <icmp.h>
#include <lock.h>
#include <util/AutoLock.h>
#include <util/DoublyLinkedList.h>
@@ -24,7 +26,9 @@
#include <NetUtilities.h>
#include <ProtocolUtilities.h>
#include <algorithm>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <new>
#include <stdlib.h>
#include <string.h>
@@ -167,7 +171,8 @@ public:
void Ref() { fEndpointCount++; }
bool Put() { fEndpointCount--; return fEndpointCount == 0; }
status_t DemuxIncomingBuffer(net_buffer *buffer);
status_t DemuxIncomingBuffer(net_buffer* buffer);
status_t DeliverError(status_t error, net_buffer* buffer);
status_t BindEndpoint(UdpEndpoint *endpoint, const sockaddr *address);
status_t ConnectEndpoint(UdpEndpoint *endpoint, const sockaddr *address);
@@ -183,6 +188,7 @@ private:
UdpEndpoint *_FindActiveEndpoint(const sockaddr *ourAddress,
const sockaddr *peerAddress);
UdpEndpoint* _EndpointFor(net_buffer* buffer);
status_t _DemuxBroadcast(net_buffer *buffer);
status_t _DemuxUnicast(net_buffer *buffer);
@@ -213,25 +219,28 @@ typedef DoublyLinkedList<UdpDomainSupport> UdpDomainList;
class UdpEndpointManager {
public:
UdpEndpointManager();
~UdpEndpointManager();
UdpEndpointManager();
~UdpEndpointManager();
status_t ReceiveData(net_buffer *buffer);
status_t Deframe(net_buffer *buffer);
status_t InitCheck() const;
UdpDomainSupport *OpenEndpoint(UdpEndpoint *endpoint);
status_t FreeEndpoint(UdpDomainSupport *domain);
status_t ReceiveData(net_buffer* buffer);
status_t ReceiveError(status_t error,
net_buffer* buffer);
status_t Deframe(net_buffer* buffer);
status_t InitCheck() const;
UdpDomainSupport* OpenEndpoint(UdpEndpoint* endpoint);
status_t FreeEndpoint(UdpDomainSupport* domain);
static int DumpEndpoints(int argc, char *argv[]);
static int DumpEndpoints(int argc, char *argv[]);
private:
UdpDomainSupport *_GetDomain(net_domain *domain, bool create);
UdpDomainSupport* _GetDomain(net_domain *domain, bool create);
UdpDomainSupport* _GetDomain(net_buffer* buffer);
mutex fLock;
status_t fStatus;
UdpDomainList fDomains;
mutex fLock;
status_t fStatus;
UdpDomainList fDomains;
};
@@ -240,6 +249,7 @@ static UdpEndpointManager *sUdpEndpointManager;
net_buffer_module_info *gBufferModule;
net_datalink_module_info *gDatalinkModule;
net_stack_module_info *gStackModule;
net_socket_module_info *gSocketModule;
// #pragma mark -
@@ -273,19 +283,38 @@ UdpDomainSupport::Init()
status_t
UdpDomainSupport::DemuxIncomingBuffer(net_buffer *buffer)
{
// NOTE multicast is delivered directly to the endpoint
// NOTE: multicast is delivered directly to the endpoint
MutexLocker _(fLock);
if (buffer->flags & MSG_BCAST)
if ((buffer->flags & MSG_BCAST) != 0)
return _DemuxBroadcast(buffer);
else if (buffer->flags & MSG_MCAST)
if ((buffer->flags & MSG_MCAST) != 0)
return B_ERROR;
return _DemuxUnicast(buffer);
}
status_t
UdpDomainSupport::DeliverError(status_t error, net_buffer* buffer)
{
if ((buffer->flags & (MSG_BCAST | MSG_MCAST)) != 0)
return B_ERROR;
MutexLocker _(fLock);
// RFC 1122 4.1.3.3:
// TODO: Pass to the application layer all ICMP error messages
UdpEndpoint* endpoint = _EndpointFor(buffer);
if (endpoint != NULL)
gSocketModule->notify(endpoint->Socket(), B_SELECT_ERROR, error);
gBufferModule->free(buffer);
return B_OK;
}
status_t
UdpDomainSupport::BindEndpoint(UdpEndpoint *endpoint,
const sockaddr *address)
@@ -479,6 +508,37 @@ UdpDomainSupport::_FindActiveEndpoint(const sockaddr *ourAddress,
}
UdpEndpoint*
UdpDomainSupport::_EndpointFor(net_buffer* buffer)
{
ASSERT_LOCKED_MUTEX(&fLock);
struct sockaddr *peerAddr = buffer->source;
struct sockaddr *localAddr = buffer->destination;
// look for full (most special) match:
UdpEndpoint* endpoint = _FindActiveEndpoint(localAddr, peerAddr);
if (endpoint != NULL)
return endpoint;
// look for endpoint matching local address & port:
endpoint = _FindActiveEndpoint(localAddr, NULL);
if (endpoint != NULL)
return endpoint;
// look for endpoint matching peer address & port and local port:
SocketAddressStorage local(AddressModule());
local.SetToEmpty();
local.SetPort(AddressModule()->get_port(localAddr));
endpoint = _FindActiveEndpoint(*local, peerAddr);
if (endpoint != NULL)
return endpoint;
// last chance: look for endpoint matching local port only:
return _FindActiveEndpoint(*local, NULL);
}
status_t
UdpDomainSupport::_DemuxBroadcast(net_buffer *buffer)
{
@@ -529,32 +589,11 @@ UdpDomainSupport::_DemuxBroadcast(net_buffer *buffer)
status_t
UdpDomainSupport::_DemuxUnicast(net_buffer *buffer)
{
struct sockaddr *peerAddr = buffer->source;
struct sockaddr *localAddr = buffer->destination;
TRACE_DOMAIN("_DemuxUnicast(%p)", buffer);
UdpEndpoint *endpoint;
// look for full (most special) match:
endpoint = _FindActiveEndpoint(localAddr, peerAddr);
if (!endpoint) {
// look for endpoint matching local address & port:
endpoint = _FindActiveEndpoint(localAddr, NULL);
if (!endpoint) {
// look for endpoint matching peer address & port and local port:
SocketAddressStorage local(AddressModule());
local.SetToEmpty();
local.SetPort(AddressModule()->get_port(localAddr));
endpoint = _FindActiveEndpoint(*local, peerAddr);
if (!endpoint) {
// last chance: look for endpoint matching local port only:
endpoint = _FindActiveEndpoint(*local, NULL);
}
}
}
if (!endpoint) {
TRACE_DOMAIN("_DemuxBroadcast(%p) - no matching endpoint found!", buffer);
UdpEndpoint* endpoint = _EndpointFor(buffer);
if (endpoint == NULL) {
TRACE_DOMAIN("_DemuxUnicast(%p) - no matching endpoint found!", buffer);
return B_NAME_NOT_FOUND;
}
@@ -650,37 +689,25 @@ UdpEndpointManager::DumpEndpoints(int argc, char *argv[])
status_t
UdpEndpointManager::ReceiveData(net_buffer *buffer)
{
TRACE_EPM("ReceiveData(%p [%" B_PRIu32 " bytes])", buffer, buffer->size);
UdpDomainSupport* domainSupport = _GetDomain(buffer);
if (domainSupport == NULL) {
// we don't instantiate domain supports in the receiving path, as
// we are only interested in delivering data to existing sockets.
return B_ERROR;
}
status_t status = Deframe(buffer);
if (status < B_OK)
if (status != B_OK)
return status;
TRACE_EPM("ReceiveData(%p [%ld bytes])", buffer, buffer->size);
net_domain *domain = buffer->interface->domain;
UdpDomainSupport *domainSupport = NULL;
{
MutexLocker _(fLock);
domainSupport = _GetDomain(domain, false);
// TODO we don't want to hold to the manager's lock
// during the whole RX path, we may not hold an
// endpoint's lock with the manager lock held.
// But we should increase the domain's refcount
// here.
}
if (domainSupport == NULL) {
// we don't instantiate domain supports in the
// RX path as we are only interested in delivering
// data to existing sockets.
return B_ERROR;
}
status = domainSupport->DemuxIncomingBuffer(buffer);
if (status < B_OK) {
if (status != B_OK) {
TRACE_EPM(" ReceiveData(): no endpoint.");
// TODO: send ICMP-error
// Send port unreachable error
domainSupport->Domain()->module->error_reply(NULL, buffer,
icmp_encode(ICMP_TYPE_UNREACH, ICMP_CODE_PORT_UNREACH), NULL);
return B_ERROR;
}
@@ -689,6 +716,48 @@ UdpEndpointManager::ReceiveData(net_buffer *buffer)
}
status_t
UdpEndpointManager::ReceiveError(status_t error, net_buffer* buffer)
{
TRACE_EPM("ReceiveError(code %" B_PRId32 " %p [%" B_PRIu32 " bytes])",
error, buffer, buffer->size);
// We only really need the port information
if (buffer->size < 4)
return B_BAD_VALUE;
UdpDomainSupport* domainSupport = _GetDomain(buffer);
if (domainSupport == NULL) {
// we don't instantiate domain supports in the receiving path, as
// we are only interested in delivering data to existing sockets.
return B_ERROR;
}
// Deframe the buffer manually, as we usually only get 8 bytes from the
// original packet
udp_header header;
if (gBufferModule->read(buffer, 0, &header,
std::min(buffer->size, sizeof(udp_header))) != B_OK)
return B_BAD_VALUE;
net_domain* domain = buffer->interface->domain;
net_address_module_info* addressModule = domain->address_module;
SocketAddress source(addressModule, buffer->source);
SocketAddress destination(addressModule, buffer->destination);
source.SetPort(header.source_port);
destination.SetPort(header.destination_port);
status_t status = domainSupport->DeliverError(error, buffer);
if (status != B_OK)
return status;
gBufferModule->free(buffer);
return B_OK;
}
status_t
UdpEndpointManager::Deframe(net_buffer *buffer)
{
@@ -806,6 +875,21 @@ UdpEndpointManager::_GetDomain(net_domain *domain, bool create)
}
UdpDomainSupport*
UdpEndpointManager::_GetDomain(net_buffer* buffer)
{
if (buffer->interface == NULL)
return NULL;
MutexLocker _(fLock);
return _GetDomain(buffer->interface->domain, false);
// TODO: we don't want to hold to the manager's lock during the
// whole RX path, we may not hold an endpoint's lock with the
// manager lock held.
// But we should increase the domain's refcount here.
}
// #pragma mark -
@@ -1161,9 +1245,46 @@ udp_deliver_data(net_protocol *protocol, net_buffer *buffer)
status_t
udp_error(uint32 code, net_buffer *data)
udp_error_received(uint32 code, net_buffer* buffer)
{
return B_ERROR;
uint8 icmpType, icmpCode;
icmp_decode(code, icmpType, icmpCode);
status_t error = B_OK;
switch (icmpType) {
case ICMP_TYPE_UNREACH:
if (icmpCode == ICMP_CODE_NET_UNREACH)
error = ENETUNREACH;
else if (icmpCode == ICMP_CODE_HOST_UNREACH)
error = EHOSTUNREACH;
else if (icmpCode == ICMP_CODE_SOURCE_ROUTE_FAIL)
error = EOPNOTSUPP;
else if (icmpCode == ICMP_CODE_PROTO_UNREACH)
error = ENOPROTOOPT;
else if (icmpCode == ICMP_CODE_PORT_UNREACH)
error = ECONNREFUSED;
else if (icmpCode == ICMP_CODE_FRAG_NEEDED)
error = EMSGSIZE;
else
error = EOPNOTSUPP;
break;
case ICMP_TYPE_TIME_EXCEEDED:
error = EHOSTUNREACH;
break;
case ICMP_TYPE_PARAM_PROBLEM:
error = EPROTO;
break;
case ICMP_TYPE_SOURCE_QUENCH:
default:
// ignore them
break;
}
if (error != B_OK)
sUdpEndpointManager->ReceiveError(error, buffer);
gBufferModule->free(buffer);
return B_OK;
}
@@ -1308,7 +1429,7 @@ net_protocol_module_info sUDPModule = {
udp_get_mtu,
udp_receive_data,
udp_deliver_data,
udp_error,
udp_error_received,
udp_error_reply,
NULL, // add_ancillary_data()
NULL, // process_ancillary_data()
@@ -1321,6 +1442,7 @@ module_dependency module_dependencies[] = {
{NET_STACK_MODULE_NAME, (module_info **)&gStackModule},
{NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule},
{NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule},
{NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule},
{}
};