Author: axeld Date: 2010-07-21 16:57:28 +0200 (Wed, 21 Jul 2010) New Revision: 37651 Changeset: http://dev.haiku-os.org/changeset/37651 Modified: haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp Log: * 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). Modified: haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp =================================================================== --- haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp 2010-07-21 14:54:24 UTC (rev 37650) +++ haiku/trunk/src/add-ons/kernel/network/protocols/udp/udp.cpp 2010-07-21 14:57:28 UTC (rev 37651) @@ -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 @@ 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 @@ 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 @@ 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 @@ net_buffer_module_info *gBufferModule; net_datalink_module_info *gDatalinkModule; net_stack_module_info *gStackModule; +net_socket_module_info *gSocketModule; // #pragma mark - @@ -273,13 +283,12 @@ 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); @@ -287,6 +296,26 @@ 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 @@ } +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 @@ 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,40 +689,70 @@ 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); + status = domainSupport->DemuxIncomingBuffer(buffer); + if (status != B_OK) { + TRACE_EPM(" ReceiveData(): no endpoint."); + // Send port unreachable error + domainSupport->Domain()->module->error_reply(NULL, buffer, + icmp_encode(ICMP_TYPE_UNREACH, ICMP_CODE_PORT_UNREACH), NULL); + return B_ERROR; + } - net_domain *domain = buffer->interface->domain; + gBufferModule->free(buffer); + return B_OK; +} - 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. - } +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 - // RX path as we are only interested in delivering - // data to existing sockets. + // 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 = domainSupport->DemuxIncomingBuffer(buffer); - if (status < B_OK) { - TRACE_EPM(" ReceiveData(): no endpoint."); - // TODO: send ICMP-error - 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; } @@ -806,6 +875,21 @@ } +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 @@ 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 @@ 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 @@ {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}, {} };