From c35b04de314ab7f18763546366ea30eb8ae53997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 2 Nov 2006 17:27:13 +0000 Subject: [PATCH] * Moved the TCPConnection class into its own file. * Added some missing result checks, mostly for allocations. * Fixed a wrong precendence with the ?: operator * Some minor cleanup. * Renamed sBufferModule to gBufferModule - the header expects it to be a global, so it should be named like one. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19178 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/net/NetBufferUtilities.h | 4 +- .../network/datalink_protocols/arp/arp.cpp | 14 +- .../ethernet_frame/ethernet_frame.cpp | 4 +- .../loopback_frame/loopback_frame.cpp | 4 +- .../network/devices/ethernet/ethernet.cpp | 18 +- .../network/devices/loopback/loopback.cpp | 4 +- .../kernel/network/protocols/icmp/icmp.cpp | 18 +- .../kernel/network/protocols/ipv4/ipv4.cpp | 50 +- .../kernel/network/protocols/tcp/Jamfile | 1 + .../network/protocols/tcp/TCPConnection.cpp | 811 +++++++++++++++++ .../network/protocols/tcp/TCPConnection.h | 846 ++---------------- .../kernel/network/protocols/tcp/tcp.cpp | 261 +++--- .../kernel/network/protocols/tcp/tcp.h | 32 +- .../kernel/network/protocols/udp/udp.cpp | 16 +- 14 files changed, 1100 insertions(+), 983 deletions(-) create mode 100644 src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp diff --git a/headers/private/net/NetBufferUtilities.h b/headers/private/net/NetBufferUtilities.h index a3734a4d5c..d33bfe1a3c 100644 --- a/headers/private/net/NetBufferUtilities.h +++ b/headers/private/net/NetBufferUtilities.h @@ -9,11 +9,11 @@ #include -extern net_buffer_module_info *sBufferModule; +extern net_buffer_module_info *gBufferModule; class NetBufferModuleGetter { public: - static net_buffer_module_info *Get() { return sBufferModule; } + static net_buffer_module_info *Get() { return gBufferModule; } }; //! A class to retrieve and remove a header from a buffer diff --git a/src/add-ons/kernel/network/datalink_protocols/arp/arp.cpp b/src/add-ons/kernel/network/datalink_protocols/arp/arp.cpp index 92fb9e6531..e41351d713 100644 --- a/src/add-ons/kernel/network/datalink_protocols/arp/arp.cpp +++ b/src/add-ons/kernel/network/datalink_protocols/arp/arp.cpp @@ -98,7 +98,7 @@ struct arp_protocol : net_datalink_protocol { static void arp_timer(struct net_timer *timer, void *data); -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; static net_stack_module_info *sStackModule; static hash_table *sCache; static benaphore sCacheLock; @@ -242,7 +242,7 @@ arp_update_entry(in_addr_t protocolAddress, sockaddr_dl *hardwareAddress, } if (entry->request_buffer != NULL) { - sBufferModule->free(entry->request_buffer); + gBufferModule->free(entry->request_buffer); entry->request_buffer = NULL; } @@ -401,7 +401,7 @@ arp_receive(void *cookie, net_buffer *buffer) return B_ERROR; } - sBufferModule->free(buffer); + gBufferModule->free(buffer); return B_OK; } @@ -447,7 +447,7 @@ arp_timer(struct net_timer *timer, void *data) if (entry->timer_state < ARP_STATE_LAST_REQUEST) { // we'll still need our buffer, so in order to prevent it being // freed by a successful send, we need to clone it - request = sBufferModule->clone(request, true); + request = gBufferModule->clone(request, true); if (request == NULL) { // cloning failed - that means we won't be able to send as // many requests as originally planned @@ -460,7 +460,7 @@ arp_timer(struct net_timer *timer, void *data) status_t status = entry->protocol->next->module->send_data( entry->protocol->next, request); if (status < B_OK) - sBufferModule->free(request); + gBufferModule->free(request); if (entry->timer_state == ARP_STATE_LAST_REQUEST) { // buffer has been freed on send @@ -535,7 +535,7 @@ arp_resolve(net_datalink_protocol *protocol, in_addr_t address, arp_entry **_ent // prepare ARP request - entry->request_buffer = sBufferModule->create(256); + entry->request_buffer = gBufferModule->create(256); if (entry->request_buffer == NULL) { // TODO: do something with the entry return B_NO_MEMORY; @@ -725,7 +725,7 @@ arp_init() status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule); if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) goto err1; diff --git a/src/add-ons/kernel/network/datalink_protocols/ethernet_frame/ethernet_frame.cpp b/src/add-ons/kernel/network/datalink_protocols/ethernet_frame/ethernet_frame.cpp index a46bc8a94e..002aa99dfd 100644 --- a/src/add-ons/kernel/network/datalink_protocols/ethernet_frame/ethernet_frame.cpp +++ b/src/add-ons/kernel/network/datalink_protocols/ethernet_frame/ethernet_frame.cpp @@ -30,7 +30,7 @@ struct ethernet_frame_protocol : net_datalink_protocol { static const uint8 kBroadcastAddress[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; int32 @@ -174,7 +174,7 @@ ethernet_frame_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: - return get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + return get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); case B_MODULE_UNINIT: put_module(NET_BUFFER_MODULE_NAME); return B_OK; diff --git a/src/add-ons/kernel/network/datalink_protocols/loopback_frame/loopback_frame.cpp b/src/add-ons/kernel/network/datalink_protocols/loopback_frame/loopback_frame.cpp index 25dc21ccac..c7af0a51a5 100644 --- a/src/add-ons/kernel/network/datalink_protocols/loopback_frame/loopback_frame.cpp +++ b/src/add-ons/kernel/network/datalink_protocols/loopback_frame/loopback_frame.cpp @@ -28,7 +28,7 @@ struct loopback_frame_protocol : net_datalink_protocol { }; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; int32 @@ -135,7 +135,7 @@ loopback_frame_std_ops(int32 op, ...) { switch (op) { case B_MODULE_INIT: - return get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + return get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); case B_MODULE_UNINIT: put_module(NET_BUFFER_MODULE_NAME); return B_OK; diff --git a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp index 855f008dc5..d9413d23b3 100644 --- a/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp +++ b/src/add-ons/kernel/network/devices/ethernet/ethernet.cpp @@ -29,7 +29,7 @@ struct ethernet_device : net_device { }; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; status_t @@ -41,7 +41,7 @@ ethernet_init(const char *name, net_device **_device) || !strcmp(name, "/dev/net/userland_server")) return B_BAD_VALUE; - status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status_t status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) return status; @@ -134,13 +134,13 @@ dprintf("try to send ethernet packet of %lu bytes (flags %ld):\n", buffer->size, if (buffer->size > device->frame_size || buffer->size < ETHER_HEADER_LENGTH) return B_BAD_VALUE; - if (sBufferModule->count_iovecs(buffer) > 1) { + if (gBufferModule->count_iovecs(buffer) > 1) { dprintf("scattered I/O is not yet supported by ethernet device.\n"); return B_NOT_SUPPORTED; } struct iovec iovec; - sBufferModule->get_iovecs(buffer, &iovec, 1); + gBufferModule->get_iovecs(buffer, &iovec, 1); dump_block((const char *)iovec.iov_base, buffer->size, " "); ssize_t bytesWritten = write(device->fd, iovec.iov_base, iovec.iov_len); @@ -153,7 +153,7 @@ dprintf("sent: %ld\n", bytesWritten); device->stats.send.packets++; device->stats.send.bytes += bytesWritten; - sBufferModule->free(buffer); + gBufferModule->free(buffer); return B_OK; } @@ -164,7 +164,7 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer) ethernet_device *device = (ethernet_device *)_device; // TODO: better header space - net_buffer *buffer = sBufferModule->create(256); + net_buffer *buffer = gBufferModule->create(256); if (buffer == NULL) return ENOBUFS; @@ -176,7 +176,7 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer) ssize_t bytesRead; void *data; - status_t status = sBufferModule->append_size(buffer, device->frame_size, &data); + status_t status = gBufferModule->append_size(buffer, device->frame_size, &data); if (status == B_OK && data == NULL) { dprintf("scattered I/O is not yet supported by ethernet device.\n"); status = B_NOT_SUPPORTED; @@ -191,7 +191,7 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer) goto err; } - status = sBufferModule->trim(buffer, bytesRead); + status = gBufferModule->trim(buffer, bytesRead); if (status < B_OK) { device->stats.receive.dropped++; goto err; @@ -204,7 +204,7 @@ ethernet_receive_data(net_device *_device, net_buffer **_buffer) return B_OK; err: - sBufferModule->free(buffer); + gBufferModule->free(buffer); return status; } diff --git a/src/add-ons/kernel/network/devices/loopback/loopback.cpp b/src/add-ons/kernel/network/devices/loopback/loopback.cpp index d7ba88c951..53e79d71ba 100644 --- a/src/add-ons/kernel/network/devices/loopback/loopback.cpp +++ b/src/add-ons/kernel/network/devices/loopback/loopback.cpp @@ -25,7 +25,7 @@ struct loopback_device : net_device { }; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; static struct net_stack_module_info *sStackModule; @@ -72,7 +72,7 @@ loopback_init(const char *name, net_device **_device) if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) goto err1; diff --git a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp index 83dbb53318..5111721240 100644 --- a/src/add-ons/kernel/network/protocols/icmp/icmp.cpp +++ b/src/add-ons/kernel/network/protocols/icmp/icmp.cpp @@ -54,7 +54,7 @@ struct icmp_protocol : net_protocol { static net_stack_module_info *sStackModule; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; net_protocol * @@ -214,8 +214,8 @@ icmp_receive_data(net_buffer *buffer) dprintf(" got type %u, code %u, checksum %u\n", header.type, header.code, ntohs(header.checksum)); - dprintf(" computed checksum: %ld\n", sBufferModule->checksum(buffer, 0, buffer->size, true)); - if (sBufferModule->checksum(buffer, 0, buffer->size, true) != 0) + dprintf(" 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) { @@ -232,7 +232,7 @@ icmp_receive_data(net_buffer *buffer) if (domain == NULL || domain->module == NULL) break; - net_buffer *reply = sBufferModule->duplicate(buffer); + net_buffer *reply = gBufferModule->duplicate(buffer); if (reply == NULL) return B_NO_MEMORY; @@ -242,26 +242,26 @@ icmp_receive_data(net_buffer *buffer) // There already is an ICMP header, and we'll reuse it icmp_header *header; - status_t status = sBufferModule->direct_access(reply, + status_t status = gBufferModule->direct_access(reply, 0, sizeof(icmp_header), (void **)&header); if (status == B_OK) { header->type = ICMP_TYPE_ECHO_REPLY; header->code = 0; header->checksum = 0; - header->checksum = sBufferModule->checksum(reply, 0, reply->size, true); + header->checksum = gBufferModule->checksum(reply, 0, reply->size, true); } if (status == B_OK) status = domain->module->send_data(NULL, reply); if (status < B_OK) { - sBufferModule->free(reply); + gBufferModule->free(reply); return status; } } } - sBufferModule->free(buffer); + gBufferModule->free(buffer); return B_OK; } @@ -293,7 +293,7 @@ icmp_std_ops(int32 op, ...) status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule); if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) { put_module(NET_STACK_MODULE_NAME); return status; diff --git a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp index ee5b29d40c..b7ee50b657 100644 --- a/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp +++ b/src/add-ons/kernel/network/protocols/ipv4/ipv4.cpp @@ -146,7 +146,7 @@ extern net_protocol_module_info gIPv4Module; static struct net_domain *sDomain; static net_datalink_module_info *sDatalinkModule; static net_stack_module_info *sStackModule; -struct net_buffer_module_info *sBufferModule; +struct net_buffer_module_info *gBufferModule; static int32 sPacketID; static RawSocketList sRawSockets; static benaphore sRawSocketsLock; @@ -190,7 +190,7 @@ RawSocket::Read(size_t numBytes, uint32 flags, bigtime_t timeout, if (numBytes < buffer->size) { // discard any data behind the amount requested - sBufferModule->trim(buffer, numBytes); + gBufferModule->trim(buffer, numBytes); } *_buffer = buffer; @@ -209,7 +209,7 @@ status_t RawSocket::Write(net_buffer *source) { // we need to make a clone for that buffer and pass it to the socket - net_buffer *buffer = sBufferModule->clone(source, false); + net_buffer *buffer = gBufferModule->clone(source, false); TRACE(("ipv4::RawSocket::Write(): cloned buffer %p\n", buffer)); if (buffer == NULL) return B_NO_MEMORY; @@ -218,7 +218,7 @@ RawSocket::Write(net_buffer *source) if (status >= B_OK) sStackModule->notify_socket(fSocket, B_SELECT_READ, BytesAvailable()); else - sBufferModule->free(buffer); + gBufferModule->free(buffer); return status; } @@ -246,7 +246,7 @@ FragmentPacket::~FragmentPacket() ipv4_fragment *fragment; while ((fragment = fFragments.RemoveHead()) != NULL) { if (fragment->buffer != NULL) - sBufferModule->free(fragment->buffer); + gBufferModule->free(fragment->buffer); delete fragment; } } @@ -283,7 +283,7 @@ FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer *buffer, if (previous != NULL && previous->start <= start && previous->end >= end) { // we do, so we can just drop this fragment - sBufferModule->free(buffer); + gBufferModule->free(buffer); return B_OK; } @@ -293,12 +293,12 @@ FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer *buffer, if (previous != NULL && previous->end > start) { TRACE((" remove header %d bytes\n", previous->end - start)); - sBufferModule->remove_header(buffer, previous->end - start); + gBufferModule->remove_header(buffer, previous->end - start); start = previous->end; } if (next != NULL && next->start < end) { TRACE((" remove trailer %d bytes\n", next->start - end)); - sBufferModule->remove_trailer(buffer, next->start - end); + gBufferModule->remove_trailer(buffer, next->start - end); end = next->start; } @@ -308,7 +308,7 @@ FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer *buffer, // report an error (in which case we're not responsible for freeing it) if (previous != NULL && previous->end == start) { - status_t status = sBufferModule->merge(buffer, previous->buffer, false); + status_t status = gBufferModule->merge(buffer, previous->buffer, false); TRACE((" merge previous: %s\n", strerror(status))); if (status < B_OK) return status; @@ -328,7 +328,7 @@ FragmentPacket::AddFragment(uint16 start, uint16 end, net_buffer *buffer, return B_OK; } else if (next != NULL && next->start == end) { - status_t status = sBufferModule->merge(buffer, next->buffer, true); + status_t status = gBufferModule->merge(buffer, next->buffer, true); TRACE((" merge next: %s\n", strerror(status))); if (status < B_OK) return status; @@ -392,10 +392,10 @@ FragmentPacket::Reassemble(net_buffer *to) if (buffer != NULL) { status_t status; if (to == fragment->buffer) { - status = sBufferModule->merge(fragment->buffer, buffer, false); + status = gBufferModule->merge(fragment->buffer, buffer, false); buffer = fragment->buffer; } else - status = sBufferModule->merge(buffer, fragment->buffer, true); + status = gBufferModule->merge(buffer, fragment->buffer, true); if (status < B_OK) return status; } else @@ -534,7 +534,7 @@ reassemble_fragments(const ipv4_header &header, net_buffer **_buffer) // Remove header unless this is the first fragment if (start != 0) - sBufferModule->remove_header(buffer, header.HeaderLength()); + gBufferModule->remove_header(buffer, header.HeaderLength()); status = packet->AddFragment(start, end, buffer, lastFragment); if (status != B_OK) @@ -580,7 +580,7 @@ send_fragments(ipv4_protocol *protocol, struct net_route *route, uint32 fragmentOffset = 0; status_t status = B_OK; - net_buffer *headerBuffer = sBufferModule->split(buffer, headerLength); + net_buffer *headerBuffer = gBufferModule->split(buffer, headerLength); if (headerBuffer == NULL) return B_NO_MEMORY; @@ -610,7 +610,7 @@ send_fragments(ipv4_protocol *protocol, struct net_route *route, net_buffer *fragmentBuffer; if (!lastFragment) { - fragmentBuffer = sBufferModule->split(buffer, fragmentLength); + fragmentBuffer = gBufferModule->split(buffer, fragmentLength); fragmentOffset += fragmentLength; } else fragmentBuffer = buffer; @@ -621,7 +621,7 @@ send_fragments(ipv4_protocol *protocol, struct net_route *route, } // copy header to fragment - status = sBufferModule->prepend(fragmentBuffer, header, headerLength); + status = gBufferModule->prepend(fragmentBuffer, header, headerLength); // send fragment if (status == B_OK) @@ -633,12 +633,12 @@ send_fragments(ipv4_protocol *protocol, struct net_route *route, } if (status < B_OK) { - sBufferModule->free(fragmentBuffer); + gBufferModule->free(fragmentBuffer); break; } } - sBufferModule->free(headerBuffer); + gBufferModule->free(headerBuffer); return status; } @@ -884,7 +884,7 @@ ipv4_send_routed_data(net_protocol *_protocol, struct net_route *route, // always use the actual used source address header.destination = ((sockaddr_in *)&buffer->destination)->sin_addr.s_addr; - header.checksum = sBufferModule->checksum(buffer, 0, sizeof(ipv4_header), true); + header.checksum = gBufferModule->checksum(buffer, 0, sizeof(ipv4_header), true); dump_ipv4_header(header); bufferHeader.Detach(); @@ -892,8 +892,8 @@ ipv4_send_routed_data(net_protocol *_protocol, struct net_route *route, } TRACE(("header chksum: %ld, buffer checksum: %ld\n", - sBufferModule->checksum(buffer, 0, sizeof(ipv4_header), true), - sBufferModule->checksum(buffer, 0, buffer->size, true))); + gBufferModule->checksum(buffer, 0, sizeof(ipv4_header), true), + gBufferModule->checksum(buffer, 0, buffer->size, true))); TRACE(("destination-IP: buffer=%p addr=%p %08lx\n", buffer, &buffer->destination, ntohl(((sockaddr_in *)&buffer->destination)->sin_addr.s_addr))); @@ -1007,7 +1007,7 @@ ipv4_receive_data(net_buffer *buffer) return B_BAD_DATA; // TODO: would be nice to have a direct checksum function somewhere - if (sBufferModule->checksum(buffer, 0, headerLength, true) != 0) + if (gBufferModule->checksum(buffer, 0, headerLength, true) != 0) return B_BAD_DATA; struct sockaddr_in &source = *(struct sockaddr_in *)&buffer->source; @@ -1036,7 +1036,7 @@ ipv4_receive_data(net_buffer *buffer) uint8 protocol = buffer->protocol = header.protocol; // remove any trailing/padding data - status_t status = sBufferModule->trim(buffer, packetLength); + status_t status = gBufferModule->trim(buffer, packetLength); if (status < B_OK) return status; @@ -1067,7 +1067,7 @@ ipv4_receive_data(net_buffer *buffer) raw_receive_data(buffer); } - sBufferModule->remove_header(buffer, headerLength); + gBufferModule->remove_header(buffer, headerLength); // the header is of variable size and may include IP options // (that we ignore for now) @@ -1111,7 +1111,7 @@ init_ipv4() status_t status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule); if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) goto err1; status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&sDatalinkModule); diff --git a/src/add-ons/kernel/network/protocols/tcp/Jamfile b/src/add-ons/kernel/network/protocols/tcp/Jamfile index 54de810d84..9e41ebf221 100644 --- a/src/add-ons/kernel/network/protocols/tcp/Jamfile +++ b/src/add-ons/kernel/network/protocols/tcp/Jamfile @@ -14,6 +14,7 @@ UsePrivateHeaders kernel net ; KernelAddon tcp : tcp.cpp + TCPConnection.cpp ; # Installation diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp new file mode 100644 index 0000000000..dc13ccdb25 --- /dev/null +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp @@ -0,0 +1,811 @@ +/* + * Copyright 2006, Haiku, Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Andrew Galante, haiku.galante@gmail.com + * Axel Dörfler, axeld@pinc-software.de + */ + + +#include "TCPConnection.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + + +#define TRACE_TCP +#ifdef TRACE_TCP +# define TRACE(x) dprintf x +#else +# define TRACE(x) +#endif + +// Initial estimate for packet round trip time (RTT) +#define TCP_INITIAL_RTT 120000000LL + +// Estimate for Maximum segment lifetime in the internet +#define TCP_MAX_SEGMENT_LIFETIME (2 * TCP_INITIAL_RTT) + +// keep maximum buffer sizes < max net_buffer size for now +#define TCP_MAX_SEND_BUF 1024 +#define TCP_MAX_RECV_BUF TCP_MAX_SEND_BUF + +#define TCP_IS_GOOD_ACK(x) (fLastByteAckd > fNextByteToSend ? \ + ((x) >= fLastByteAckd || (x) <= fNextByteToSend) : \ + ((x) >= fLastByteAckd && (x) <= fNextByteToSend)) + +#define TCP_IS_GOOD_SEQ(x,y) (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF ? \ + (x) >= fNextByteToRead && (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF : \ + ((x) >= fNextByteToRead || (x) <= fNextByteToRead + TCP_MAX_RECV_BUF) && \ + ((x) + (y) >= fNextByteToRead || (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF)) + +struct tcp_segment { + struct list_link link; + + net_buffer *buffer; + bigtime_t time; + uint32 sequence; + net_timer timer; + bool timed_out; + + tcp_segment(net_buffer *buffer, uint32 sequenceNumber, bigtime_t timeout); + ~tcp_segment(); +}; + + +tcp_segment::tcp_segment(net_buffer *_buffer, uint32 sequenceNumber, bigtime_t timeout) + : + buffer(_buffer), + time(system_time()), + sequence(sequenceNumber), + timed_out(false) +{ + if (timeout > 0) { + gStackModule->init_timer(&timer, &TCPConnection::ResendSegment, this); + gStackModule->set_timer(&timer, timeout); + } +} + + +tcp_segment::~tcp_segment() +{ + gStackModule->set_timer(&timer, -1); +} + + +// #pragma mark - + + +TCPConnection::TCPConnection(net_socket *socket) + : + fLastByteAckd(0), //system_time()), + fNextByteToSend(fLastByteAckd), + fNextByteToWrite(fLastByteAckd + 1), + fNextByteToRead(0), + fNextByteExpected(0), + fLastByteReceived(0), + fAvgRTT(TCP_INITIAL_RTT), + fSendBuffer(NULL), + fReceiveBuffer(NULL), + fState(CLOSED), + fError(B_OK), + fRoute(NULL) +{ + benaphore_init(&fLock, "TCPConnection"); + gStackModule->init_timer(&fTimer, _TimeWait, this); + list_init(&fReorderQueue); + list_init(&fWaitQueue); +} + + +TCPConnection::~TCPConnection() +{ + benaphore_destroy(&fLock); +} + + +status_t +TCPConnection::Open() +{ + TRACE(("%p.Open()\n", this)); + if (gAddressModule == NULL) + return B_ERROR; + TRACE(("TCP: Open(): Using Address Module %p\n", gAddressModule)); + + BenaphoreLocker lock(&fLock); + gAddressModule->set_to_empty_address((sockaddr *)&socket->address); + gAddressModule->set_port((sockaddr *)&socket->address, 0); + gAddressModule->set_to_empty_address((sockaddr *)&socket->peer); + gAddressModule->set_port((sockaddr *)&socket->peer, 0); + return B_OK; +} + + +status_t +TCPConnection::Close() +{ + BenaphoreLocker lock(&fLock); + TRACE(("TCP:%p.Close()\n", this)); + if (fState == SYN_SENT || fState == LISTEN) { + fState = CLOSED; + return B_OK; + } + tcp_state nextState = CLOSED; + if (fState == SYN_RCVD || fState == ESTABLISHED) + nextState = FIN_WAIT1; + if (fState == CLOSE_WAIT) + nextState = LAST_ACK; + status_t status = _SendQueuedData(TCP_FLG_FIN | TCP_FLG_ACK, false); + if (status != B_OK) + return status; + fState = nextState; + TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState)); + //do i need to wait until fState returns to CLOSED? + return B_OK; +} + + +status_t +TCPConnection::Free() +{ + TRACE(("TCP:%p.Free()\n", this)); + + BenaphoreLocker hashLock(&gConnectionLock); + BenaphoreLocker lock(&fLock); + tcp_connection_key key; + key.local = (sockaddr *)&socket->address; + key.peer = (sockaddr *)&socket->peer; + if (hash_lookup(gConnectionHash, &key) != NULL) { + return hash_remove(gConnectionHash, (void *)this); + } + return B_OK; +} + + +/*! + Creates and sends a SYN packet to /a address +*/ +status_t +TCPConnection::Connect(const struct sockaddr *address) +{ + TRACE(("TCP:%p.Connect() on address %s\n", this, + AddressString(gDomain, address, true).Data())); + + if (address->sa_family != AF_INET) + return EAFNOSUPPORT; + + benaphore_lock(&gConnectionLock); // want to release lock later, so no autolock + BenaphoreLocker lock(&fLock); + + // Can only call Connect from CLOSED or LISTEN states + // otherwise connection is considered already connected + if (fState != CLOSED && fState != LISTEN) { + benaphore_unlock(&gConnectionLock); + return EISCONN; + } + TRACE(("TCP: Connect(): in state %d\n", fState)); + + // get a net_route if there isn't one + if (fRoute == NULL) { + fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address); + TRACE(("TCP: Connect(): Using Route %p\n", fRoute)); + if (fRoute == NULL) { + benaphore_unlock(&gConnectionLock); + return ENETUNREACH; + } + } + + // need to associate this connection with a real address, not INADDR_ANY + if (gAddressModule->is_empty_address((sockaddr *)&socket->address, false)) { + TRACE(("TCP: Connect(): Local Address is INADDR_ANY\n")); + gAddressModule->set_to((sockaddr *)&socket->address, (sockaddr *)fRoute->interface->address); + // since most stacks terminate connections from port 0 + // use port 40000 for now. This should be moved to Bind(), and Bind() called before Connect(). + gAddressModule->set_port((sockaddr *)&socket->address, htons(40000)); + } + + // make sure connection does not already exist + tcp_connection_key key; + key.local = (sockaddr *)&socket->address; + key.peer = address; + if (hash_lookup(gConnectionHash, &key) != NULL) { + benaphore_unlock(&gConnectionLock); + return EADDRINUSE; + } + TRACE(("TCP: Connect(): connecting...\n")); + + status_t status; + gAddressModule->set_to((sockaddr *)&socket->peer, address); + status = hash_insert(gConnectionHash, (void *)this); + if (status != B_OK) { + TRACE(("TCP: Connect(): Error inserting connection into hash!\n")); + benaphore_unlock(&gConnectionLock); + return status; + } + + // done manipulating the hash, release the lock + benaphore_unlock(&gConnectionLock); + + TRACE(("TCP: Connect(): starting 3-way handshake...\n")); + // send SYN + status = _SendQueuedData(TCP_FLG_SYN, false); + if (status != B_OK) + return status; + fState = SYN_SENT; + + // TODO: Should Connect() not return until 3-way handshake is complete? + TRACE(("TCP: Connect(): Connection complete\n")); + return B_OK; +} + + +status_t +TCPConnection::Accept(struct net_socket **_acceptedSocket) +{ + TRACE(("TCP:%p.Accept()\n", this)); + return B_ERROR; +} + + +status_t +TCPConnection::Bind(sockaddr *address) +{ + TRACE(("TCP:%p.Bind() on address %s\n", this, + AddressString(gDomain, address, true).Data())); + + if (address->sa_family != AF_INET) + return EAFNOSUPPORT; + + BenaphoreLocker hashLock(&gConnectionLock); + BenaphoreLocker lock(&fLock); + + // let IP check whether there is an interface that supports the given address: + status_t status = next->module->bind(next, address); + if (status < B_OK) + return status; + + gAddressModule->set_to((sockaddr *)&socket->address, address); + // for now, leave port=0. TCP should still work 1 connection at a time + if (0) { //gAddressModule->get_port((sockaddr *)&socket->address) == 0) { + // assign ephemeral port + } else { + // TODO: Check for Socket flags + tcp_connection_key key; + key.peer = (sockaddr *)&socket->peer; + key.local = (sockaddr *)&socket->address; + if (hash_lookup(gConnectionHash, &key) == NULL) { + hash_insert(gConnectionHash, (void *)this); + } else + return EADDRINUSE; + } + + return B_OK; +} + + +status_t +TCPConnection::Unbind(struct sockaddr *address) +{ + TRACE(("TCP:%p.Unbind()\n", this )); + + BenaphoreLocker hashLock(&gConnectionLock); + BenaphoreLocker lock(&fLock); + + status_t status = hash_remove(gConnectionHash, (void *)this); + if (status != B_OK) + return status; + + gAddressModule->set_to_empty_address((sockaddr *)&socket->address); + gAddressModule->set_port((sockaddr *)&socket->address, 0); + return B_OK; +} + + +status_t +TCPConnection::Listen(int count) +{ + TRACE(("TCP:%p.Listen()\n", this)); + BenaphoreLocker lock(&fLock); + if (fState != CLOSED) + return B_ERROR; + + fState = LISTEN; + return B_OK; +} + + +status_t +TCPConnection::Shutdown(int direction) +{ + TRACE(("TCP:%p.Shutdown()\n", this)); + return B_ERROR; +} + + +/*! + Puts data contained in \a buffer into send buffer +*/ +status_t +TCPConnection::SendData(net_buffer *buffer) +{ + TRACE(("TCP:%p.SendData()\n", this)); + size_t bufferSize = buffer->size; + BenaphoreLocker lock(&fLock); + if (fSendBuffer != NULL) { + status_t status = gBufferModule->merge(fSendBuffer, buffer, true); + if (status != B_OK) + return status; + } else + fSendBuffer = buffer; + + fNextByteToWrite += bufferSize; + return _SendQueuedData(TCP_FLG_ACK, false); +} + + +status_t +TCPConnection::SendRoutedData(net_route *route, net_buffer *buffer) +{ + TRACE(("TCP:%p.SendRoutedData()\n", this)); + { + BenaphoreLocker lock(&fLock); + fRoute = route; + } + return SendData(buffer); +} + + +size_t +TCPConnection::SendAvailable() +{ + TRACE(("TCP:%p.SendAvailable()\n", this)); + BenaphoreLocker lock(&fLock); + if (fSendBuffer != NULL) + return TCP_MAX_SEND_BUF - fSendBuffer->size; + + return TCP_MAX_SEND_BUF; +} + + +status_t +TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer) +{ + TRACE(("TCP:%p.ReadData()\n", this)); + + BenaphoreLocker lock(&fLock); + + // must be in a synchronous state + if (fState != ESTABLISHED || fState != FIN_WAIT1 || fState != FIN_WAIT2) { + // is this correct semantics? +dprintf(" TCP state = %d\n", fState); + return B_ERROR; + } + +dprintf(" TCP error = %ld\n", fError); + if (fError != B_OK) + return fError; + + if (fReceiveBuffer->size < numBytes) + numBytes = fReceiveBuffer->size; + + *_buffer = gBufferModule->split(fReceiveBuffer, numBytes); + if (*_buffer == NULL) + return B_NO_MEMORY; + + return B_OK; +} + + +size_t +TCPConnection::ReadAvailable() +{ + TRACE(("TCP:%p.ReadAvailable()\n", this)); + BenaphoreLocker lock(&fLock); + if (fReceiveBuffer != NULL) + return fReceiveBuffer->size; + + return 0; +} + + +/*! + You must hold the connection's lock when calling this method +*/ +status_t +TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence) +{ + TRACE(("TCP:%p.EnqueueReceivedData(%p, %lu)\n", this, buffer, sequence)); + status_t status; + + if (sequence == fNextByteExpected) { + // first check if the received buffer meets up with the first + // segment in the ReorderQueue + tcp_segment *next; + while ((next = (tcp_segment *)list_get_first_item(&fReorderQueue)) != NULL) { + if (sequence + buffer->size >= next->sequence) { + if (sequence + buffer->size > next->sequence) { + status = gBufferModule->trim(buffer, sequence - next->sequence); + if (status != B_OK) + return status; + } + status = gBufferModule->merge(buffer, next->buffer, true); + if (status != B_OK) + return status; + list_remove_item(&fReorderQueue, next); + delete next; + } else + break; + } + + fNextByteExpected += buffer->size; + if (fReceiveBuffer == NULL) + fReceiveBuffer = buffer; + else { + status = gBufferModule->merge(fReceiveBuffer, buffer, true); + if (status < B_OK) { + fNextByteExpected -= buffer->size; + return status; + } + } + } else { + // add this buffer into the ReorderQueue in the correct place + // creating a new tcp_segment if necessary + tcp_segment *next = NULL; + do { + next = (tcp_segment *)list_get_next_item(&fReorderQueue, next); + if (next != NULL && next->sequence < sequence) + continue; + if (next != NULL && sequence + buffer->size >= next->sequence) { + // merge the new buffer with the next buffer + if (sequence + buffer->size > next->sequence) { + status = gBufferModule->trim(buffer, sequence - next->sequence); + if (status != B_OK) + return status; + } + status = gBufferModule->merge(buffer, next->buffer, true); + if (status != B_OK) + return status; + + next->buffer = buffer; + next->sequence = sequence; + break; + } + tcp_segment *segment = new(std::nothrow) tcp_segment(buffer, sequence, -1); + if (segment == NULL) + return B_NO_MEMORY; + + if (next == NULL) + list_add_item(&fReorderQueue, segment); + else + list_insert_item_before(&fReorderQueue, next, segment); + } while (next != NULL); + } + + return B_OK; +} + + +status_t +TCPConnection::ReceiveData(net_buffer *buffer) +{ + BenaphoreLocker lock(&fLock); + TRACE(("TCP:%p.ReceiveData()\n", this)); + + NetBufferHeader bufferHeader(buffer); + if (bufferHeader.Status() < B_OK) + return bufferHeader.Status(); + + tcp_header &header = bufferHeader.Data(); + + uint16 flags = 0x0; + tcp_state nextState = fState; + status_t status = B_OK; + uint32 byteAckd = ntohl(header.acknowledge_num); + uint32 byteRcvd = ntohl(header.sequence_num); + uint32 headerLength = (uint32)header.header_length << 2; + uint32 payloadLength = buffer->size - headerLength; + + TRACE(("TCP: ReceiveData(): Connection in state %d received packet %p with flags %X!\n", fState, buffer, header.flags)); + switch (fState) { + case CLOSED: + case TIME_WAIT: + gBufferModule->free(buffer); + if (header.flags & TCP_FLG_ACK) + return _Reset(byteAckd, 0); + + return _Reset(0, byteRcvd + payloadLength); + + case LISTEN: + // if packet is SYN, spawn new TCPConnection in SYN_RCVD state + // and add it to the Connection Queue. The new TCPConnection + // must continue the handshake by replying with SYN+ACK. Any + // data in the packet must go into the new TCPConnection's receive + // buffer. + // Otherwise, RST+ACK is sent. + // The current TCPConnection always remains in LISTEN state. + return B_ERROR; + + case SYN_SENT: + if (header.flags & TCP_FLG_RST) { + fError = ECONNREFUSED; + fState = CLOSED; + return B_ERROR; + } + + if (header.flags & TCP_FLG_ACK && !TCP_IS_GOOD_ACK(byteAckd)) + return _Reset(byteAckd, 0); + + if (header.flags & TCP_FLG_SYN) { + fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1; + flags |= TCP_FLG_ACK; + fLastByteAckd = byteAckd; + + // cancel resend of this segment + if (header.flags & TCP_FLG_ACK) + nextState = ESTABLISHED; + else { + nextState = SYN_RCVD; + flags |= TCP_FLG_SYN; + } + } + break; + + case SYN_RCVD: + if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd)) + fState = ESTABLISHED; + else + _Reset(byteAckd, 0); + break; + + default: + // In a synchronized state. + // first check that the received sequence number is good + if (TCP_IS_GOOD_SEQ(byteRcvd, payloadLength)) { + // If a valid RST was received, terminate the connection. + if (header.flags & TCP_FLG_RST) { + fError = ECONNREFUSED; + fState = CLOSED; + return B_ERROR; + } + + if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd) ) { + fLastByteAckd = byteAckd; + if (fLastByteAckd == fNextByteToWrite) { + if (fState == LAST_ACK ) { + nextState = CLOSED; + status = hash_remove(gConnectionHash, this); + if (status != B_OK) + return status; + } + if (fState == CLOSING) { + nextState = TIME_WAIT; + status = hash_remove(gConnectionHash, this); + if (status != B_OK) + return status; + } + if (fState == FIN_WAIT1) { + nextState = FIN_WAIT2; + } + } + } + if (header.flags & TCP_FLG_FIN) { + // other side is closing connection. change states + switch (fState) { + case ESTABLISHED: + nextState = CLOSE_WAIT; + fNextByteExpected++; + break; + case FIN_WAIT2: + nextState = TIME_WAIT; + fNextByteExpected++; + break; + case FIN_WAIT1: + if (fLastByteAckd == fNextByteToWrite) { + // our FIN has been ACKd: go to TIME_WAIT + nextState = TIME_WAIT; + status = hash_remove(gConnectionHash, this); + if (status != B_OK) + return status; + gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME); + } else + nextState = CLOSING; + fNextByteExpected++; + break; + default: + break; + } + } + flags |= TCP_FLG_ACK; + } else { + // out-of-order packet received. remind the other side of where we are + return _SendQueuedData(TCP_FLG_ACK, true); + } + break; + } + TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState)); + + // state machine is done switching states and the data is good. + // put it in the receive buffer + // TODO: This isn't the most efficient way to do it, and will need to be changed + // to deal with Silly Window Syndrome + bufferHeader.Remove(headerLength); + if (buffer->size > 0) { + status = _EnqueueReceivedData(buffer, byteRcvd); + if (status != B_OK) + return status; + } else + gBufferModule->free(buffer); + + if (fState != CLOSING && fState != LAST_ACK) { + status = _SendQueuedData(flags, false); + if (status != B_OK) + return status; + } + + fState = nextState; + return B_OK; +} + + +status_t +TCPConnection::_Reset(uint32 sequence, uint32 acknowledge) +{ + TRACE(("TCP:%p.Reset()\n", this)); + net_buffer *reply = gBufferModule->create(512); + if (reply == NULL) + return B_NO_MEMORY; + + gAddressModule->set_to((sockaddr *)&reply->source, (sockaddr *)&socket->address); + gAddressModule->set_to((sockaddr *)&reply->destination, (sockaddr *)&socket->peer); + + status_t status = add_tcp_header(reply, + TCP_FLG_RST | (acknowledge == 0 ? 0 : TCP_FLG_ACK), sequence, acknowledge, 0); + if (status != B_OK) { + gBufferModule->free(reply); + return status; + } + + TRACE(("TCP: Reset():Sending RST...\n")); + + status = next->module->send_routed_data(next, fRoute, reply); + if (status != B_OK) { + // if sending failed, we stay responsible for the buffer + gBufferModule->free(reply); + } + + return status; +} + + +/*! + Resends a sent segment (\a data) if the segment's ACK wasn't received + before the timeout (eg \a timer expired) +*/ +void +TCPConnection::ResendSegment(struct net_timer *timer, void *data) +{ + TRACE(("TCP:ResendSegment(%p)\n", data)); + if (data == NULL) + return; + + // TODO: implement me! +} + + +/*! + Sends a TCP packet with the specified \a flags. If there is any data in + the send buffer and \a empty is false, fEffectiveWindow bytes or less of it are sent as well. + Sequence and Acknowledgement numbers are filled in accordingly. + The fLock benaphore must be held before calling. +*/ +status_t +TCPConnection::_SendQueuedData(uint16 flags, bool empty) +{ + TRACE(("TCP:%p.SendQueuedData(%X,%s)\n", this, flags, empty ? "1" : "0")); + + if (fRoute == NULL) + return B_ERROR; + + net_buffer *buffer; + uint32 effectiveWindow = min_c(next->module->get_mtu(next, + (sockaddr *)&socket->address), fNextByteToWrite - fNextByteToSend); + + if (empty || effectiveWindow == 0 || fSendBuffer == NULL || fSendBuffer->size == 0) { + buffer = gBufferModule->create(256); + TRACE(("TCP: Sending Buffer %p\n", buffer)); + if (buffer == NULL) + return ENOBUFS; + } else { + buffer = fSendBuffer; + if (effectiveWindow == fSendBuffer->size) + fSendBuffer = NULL; + else + fSendBuffer = gBufferModule->split(fSendBuffer, effectiveWindow); + } + + gAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address); + gAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer); + TRACE(("TCP:%p.SendQueuedData() to address %s\n", this, + AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data())); + TRACE(("TCP:%p.SendQueuedData() from address %s\n", this, + AddressString(gDomain, (sockaddr *)&buffer->source, true).Data())); + + uint16 advertisedWindow = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead); + uint32 size = buffer->size; + + status_t status = add_tcp_header(buffer, flags, fNextByteToSend, + fNextByteExpected, advertisedWindow); + if (status != B_OK) { + gBufferModule->free(buffer); + return status; + } + + // Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN + if (TCP_FLG_SYN & flags && (fState == CLOSED || fState == LISTEN)) + fNextByteToSend++; + // Only count 1 FIN, the 1 sent when transitioning from ESTABLISHED, SYN_RCVD or CLOSE_WAIT + if (TCP_FLG_FIN & flags && (fState == SYN_RCVD || fState == ESTABLISHED || fState == CLOSE_WAIT)) + fNextByteToSend++; + fNextByteToSend += size; + +#if 0 + tcp_segment *segment = new(std::nothrow) + tcp_segment(sequenceNum, 0, 2*fAvgRTT); +#endif + + return next->module->send_routed_data(next, fRoute, buffer); +} + + +void +TCPConnection::_TimeWait(struct net_timer *timer, void *data) +{ +} + + +int +TCPConnection::Compare(void *_connection, const void *_key) +{ + const tcp_connection_key *key = (tcp_connection_key *)_key; + TCPConnection *connection= ((TCPConnection *)_connection); + + if (gAddressModule->equal_addresses_and_ports(key->local, + (sockaddr *)&connection->socket->address) + && gAddressModule->equal_addresses_and_ports(key->peer, + (sockaddr *)&connection->socket->peer)) + return 0; + + return 1; +} + + +uint32 +TCPConnection::Hash(void *_connection, const void *_key, uint32 range) +{ + if (_connection != NULL) { + TCPConnection *connection = (TCPConnection *)_connection; + return gAddressModule->hash_address_pair( + (sockaddr *)&connection->socket->address, (sockaddr *)&connection->socket->peer) % range; + } + + const tcp_connection_key *key = (tcp_connection_key *)_key; + return gAddressModule->hash_address_pair( + key->local, key->peer) % range; +} diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h index 4d78bc510a..0f26bbf8ae 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h @@ -4,818 +4,78 @@ * * Authors: * Andrew Galante, haiku.galante@gmail.com + * Axel Dörfler, axeld@pinc-software.de */ +#ifndef TCP_CONNECTION_H +#define TCP_CONNECTION_H -// Initial estimate for packet round trip time (RTT) -#define TCP_INITIAL_RTT 120000000LL -// Estimate for Maximum segment lifetime in the internet -#define TCP_MAX_SEGMENT_LIFETIME (2 * TCP_INITIAL_RTT) +#include "tcp.h" -// keep maximum buffer sizes < max net_buffer size for now -#define TCP_MAX_SEND_BUF 1024 -#define TCP_MAX_RECV_BUF TCP_MAX_SEND_BUF +#include +#include -#define TCP_IS_GOOD_ACK(x) (fLastByteAckd > fNextByteToSend ? \ - ((x) >= fLastByteAckd || (x) <= fNextByteToSend) : \ - ((x) >= fLastByteAckd && (x) <= fNextByteToSend)) - -#define TCP_IS_GOOD_SEQ(x,y) (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF ? \ - (x) >= fNextByteToRead && (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF : \ - ((x) >= fNextByteToRead || (x) <= fNextByteToRead + TCP_MAX_RECV_BUF) && \ - ((x) + (y) >= fNextByteToRead || (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF)) - -typedef struct { - const sockaddr *local; - const sockaddr *peer; -} tcp_connection_key; +#include class TCPConnection : public net_protocol { -public: - TCPConnection(net_socket *socket); - ~TCPConnection(); - - status_t Open(); - status_t Close(); - status_t Free(); - status_t Connect(const struct sockaddr *address); - status_t Accept(struct net_socket **_acceptedSocket); - status_t Bind(struct sockaddr *address); - status_t Unbind(struct sockaddr *address); - status_t Listen(int count); - status_t Shutdown(int direction); - status_t SendData(net_buffer *buffer); - status_t SendRoutedData(net_route *route, net_buffer *buffer); - size_t SendAvailable(); - status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer); - size_t ReadAvailable(); - - status_t ReceiveData(net_buffer *buffer); - - static void ResendSegment(struct net_timer *timer, void *data); - static int Compare(void *_packet, const void *_key); - static uint32 Hash(void *_packet, const void *_key, uint32 range); - static int32 HashOffset() { return offsetof(TCPConnection, fHashLink); } -private: - - class TCPSegment { public: + TCPConnection(net_socket *socket); + ~TCPConnection(); - struct list_link link; + status_t Open(); + status_t Close(); + status_t Free(); + status_t Connect(const struct sockaddr *address); + status_t Accept(struct net_socket **_acceptedSocket); + status_t Bind(struct sockaddr *address); + status_t Unbind(struct sockaddr *address); + status_t Listen(int count); + status_t Shutdown(int direction); + status_t SendData(net_buffer *buffer); + status_t SendRoutedData(net_route *route, net_buffer *buffer); + size_t SendAvailable(); + status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer); + size_t ReadAvailable(); - TCPSegment(net_buffer *buffer, uint32 sequenceNumber, bigtime_t timeout); - ~TCPSegment(); + status_t ReceiveData(net_buffer *buffer); - net_buffer *fBuffer; - bigtime_t fTime; - uint32 fSequenceNumber; - net_timer fTimer; - bool fTimedOut; - }; + static void ResendSegment(struct net_timer *timer, void *data); + static int Compare(void *_packet, const void *_key); + static uint32 Hash(void *_packet, const void *_key, uint32 range); + static int32 HashOffset() { return offsetof(TCPConnection, fHashLink); } - status_t SendQueuedData(uint16 flags, bool empty); - status_t EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber); - status_t Reset(uint32 sequenceNum, uint32 acknowledgeNum); - static void TimeWait(struct net_timer *timer, void *data); + private: + status_t _SendQueuedData(uint16 flags, bool empty); + status_t _EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber); + status_t _Reset(uint32 sequenceNum, uint32 acknowledgeNum); - uint32 fLastByteAckd; - uint32 fNextByteToSend; - uint32 fNextByteToWrite; + static void _TimeWait(struct net_timer *timer, void *data); - uint32 fNextByteToRead; - uint32 fNextByteExpected; - uint32 fLastByteReceived; + uint32 fLastByteAckd; + uint32 fNextByteToSend; + uint32 fNextByteToWrite; - bigtime_t fAvgRTT; + uint32 fNextByteToRead; + uint32 fNextByteExpected; + uint32 fLastByteReceived; - net_buffer *fSendBuffer; - net_buffer *fReceiveBuffer; + bigtime_t fAvgRTT; - struct list fReorderQueue; - struct list fWaitQueue; + net_buffer *fSendBuffer; + net_buffer *fReceiveBuffer; - TCPConnection *fHashLink; - tcp_state fState; - status_t fError; - benaphore fLock; - net_timer fTimer; + struct list fReorderQueue; + struct list fWaitQueue; - net_route *fRoute; + TCPConnection *fHashLink; + tcp_state fState; + status_t fError; + benaphore fLock; + net_timer fTimer; + + net_route *fRoute; + // TODO: don't use a net_route, but a net_route_info!!! }; - -TCPConnection::TCPSegment::TCPSegment(net_buffer *buffer, uint32 sequenceNumber, bigtime_t timeout) - : - fBuffer(buffer), - fTime(system_time()), - fSequenceNumber(sequenceNumber), - fTimedOut(false) -{ - if (timeout > 0) { - sStackModule->init_timer(&fTimer, &TCPConnection::ResendSegment, this); - sStackModule->set_timer(&fTimer, timeout); - } -} - - -TCPConnection::TCPSegment::~TCPSegment() -{ - sStackModule->set_timer(&fTimer, -1); -} - - -TCPConnection::TCPConnection(net_socket *socket) - : - fLastByteAckd(0), //system_time()), - fNextByteToSend(fLastByteAckd), - fNextByteToWrite(fLastByteAckd + 1), - fNextByteToRead(0), - fNextByteExpected(0), - fLastByteReceived(0), - fAvgRTT(TCP_INITIAL_RTT), - fSendBuffer(NULL), - fReceiveBuffer(NULL), - fState(CLOSED), - fError(B_OK), - fRoute(NULL) -{ - benaphore_init(&fLock, "TCPConnection"); - sStackModule->init_timer(&fTimer, TimeWait, this); - list_init(&fReorderQueue); - list_init(&fWaitQueue); -} - - -TCPConnection::~TCPConnection() -{ - benaphore_destroy(&fLock); -} - - -status_t -TCPConnection::Open() -{ - TRACE(("%p.Open()\n", this)); - if (sAddressModule == NULL) - return B_ERROR; - TRACE(("TCP: Open(): Using Address Module %p\n", sAddressModule)); - - BenaphoreLocker lock(&fLock); - sAddressModule->set_to_empty_address((sockaddr *)&socket->address); - sAddressModule->set_port((sockaddr *)&socket->address, 0); - sAddressModule->set_to_empty_address((sockaddr *)&socket->peer); - sAddressModule->set_port((sockaddr *)&socket->peer, 0); - return B_OK; -} - - -status_t -TCPConnection::Close() -{ - BenaphoreLocker lock(&fLock); - TRACE(("TCP:%p.Close()\n", this)); - if (fState == SYN_SENT || fState == LISTEN) { - fState = CLOSED; - return B_OK; - } - tcp_state nextState = CLOSED; - if (fState == SYN_RCVD || fState == ESTABLISHED) - nextState = FIN_WAIT1; - if (fState == CLOSE_WAIT) - nextState = LAST_ACK; - status_t status = SendQueuedData(TCP_FLG_FIN | TCP_FLG_ACK, false); - if (status != B_OK) - return status; - fState = nextState; - TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState)); - //do i need to wait until fState returns to CLOSED? - return B_OK; -} - - -status_t -TCPConnection::Free() -{ - TRACE(("TCP:%p.Free()\n", this)); - - BenaphoreLocker hashLock(&sTCPLock); - BenaphoreLocker lock(&fLock); - tcp_connection_key key; - key.local = (sockaddr *)&socket->address; - key.peer = (sockaddr *)&socket->peer; - if (hash_lookup(sTCPHash, &key) != NULL) { - return hash_remove(sTCPHash, (void *)this); - } - return B_OK; -} - - -/*! - Creates and sends a SYN packet to /a address -*/ -status_t -TCPConnection::Connect(const struct sockaddr *address) -{ - TRACE(("TCP:%p.Connect() on address %s\n", this, - AddressString(sDomain, address, true).Data())); - - if (address->sa_family != AF_INET) - return EAFNOSUPPORT; - - benaphore_lock(&sTCPLock); // want to release lock later, so no autolock - BenaphoreLocker lock(&fLock); - - // Can only call Connect from CLOSED or LISTEN states - // otherwise connection is considered already connected - if (fState != CLOSED && fState != LISTEN) { - benaphore_unlock(&sTCPLock); - return EISCONN; - } - TRACE(("TCP: Connect(): in state %d\n", fState)); - - // get a net_route if there isn't one - if (fRoute == NULL) { - fRoute = sDatalinkModule->get_route(sDomain, (sockaddr *)address); - TRACE(("TCP: Connect(): Using Route %p\n", fRoute)); - if (fRoute == NULL) { - benaphore_unlock(&sTCPLock); - return ENETUNREACH; - } - } - - // need to associate this connection with a real address, not INADDR_ANY - if (sAddressModule->is_empty_address((sockaddr *)&socket->address, false)) { - TRACE(("TCP: Connect(): Local Address is INADDR_ANY\n")); - sAddressModule->set_to((sockaddr *)&socket->address, (sockaddr *)fRoute->interface->address); - // since most stacks terminate connections from port 0 - // use port 40000 for now. This should be moved to Bind(), and Bind() called before Connect(). - sAddressModule->set_port((sockaddr *)&socket->address, htons(40000)); - } - - // make sure connection does not already exist - tcp_connection_key key; - key.local = (sockaddr *)&socket->address; - key.peer = address; - if (hash_lookup(sTCPHash, &key) != NULL) { - benaphore_unlock(&sTCPLock); - return EADDRINUSE; - } - TRACE(("TCP: Connect(): connecting...\n")); - - status_t status; - sAddressModule->set_to((sockaddr *)&socket->peer, address); - status = hash_insert(sTCPHash, (void *)this); - if (status != B_OK) { - TRACE(("TCP: Connect(): Error inserting connection into hash!\n")); - benaphore_unlock(&sTCPLock); - return status; - } - - // done manipulating the hash, release the lock - benaphore_unlock(&sTCPLock); - - TRACE(("TCP: Connect(): starting 3-way handshake...\n")); - // send SYN - status = SendQueuedData(TCP_FLG_SYN, false); - if (status != B_OK) - return status; - fState = SYN_SENT; - - // TODO: Should Connect() not return until 3-way handshake is complete? - TRACE(("TCP: Connect(): Connection complete\n")); - return B_OK; -} - - -status_t -TCPConnection::Accept(struct net_socket **_acceptedSocket) -{ - TRACE(("TCP:%p.Accept()\n", this)); - return B_ERROR; -} - - -status_t -TCPConnection::Bind(sockaddr *address) -{ - TRACE(("TCP:%p.Bind() on address %s\n", this, - AddressString(sDomain, address, true).Data())); - - if (address->sa_family != AF_INET) - return EAFNOSUPPORT; - - BenaphoreLocker hashLock(&sTCPLock); - BenaphoreLocker lock(&fLock); - - // let IP check whether there is an interface that supports the given address: - status_t status = next->module->bind(next, address); - if (status < B_OK) - return status; - - sAddressModule->set_to((sockaddr *)&socket->address, address); - // for now, leave port=0. TCP should still work 1 connection at a time - if (0) { //sAddressModule->get_port((sockaddr *)&socket->address) == 0) { - //assign ephemeral port - } else { - //TODO:Check for Socket flags - tcp_connection_key key; - key.peer = (sockaddr *)&socket->peer; - key.local = (sockaddr *)&socket->address; - if (hash_lookup(sTCPHash, &key) == NULL) { - hash_insert(sTCPHash, (void *)this); - } else - return EADDRINUSE; - } - - return B_OK; -} - - -status_t -TCPConnection::Unbind(struct sockaddr *address) -{ - TRACE(("TCP:%p.Unbind()\n", this )); - - BenaphoreLocker hashLock(&sTCPLock); - BenaphoreLocker lock(&fLock); - - status_t status = hash_remove(sTCPHash, (void *)this); - if (status != B_OK) - return status; - - sAddressModule->set_to_empty_address((sockaddr *)&socket->address); - sAddressModule->set_port((sockaddr *)&socket->address, 0); - return B_OK; -} - - -status_t -TCPConnection::Listen(int count) -{ - TRACE(("TCP:%p.Listen()\n", this)); - BenaphoreLocker lock(&fLock); - if (fState != CLOSED) - return B_ERROR; - - fState = LISTEN; - return B_OK; -} - - -status_t -TCPConnection::Shutdown(int direction) -{ - TRACE(("TCP:%p.Shutdown()\n", this)); - return B_ERROR; -} - - -/*! - Puts data contained in \a buffer into send buffer -*/ -status_t -TCPConnection::SendData(net_buffer *buffer) -{ - TRACE(("TCP:%p.SendData()\n", this)); - size_t bufferSize = buffer->size; - BenaphoreLocker lock(&fLock); - if (fSendBuffer != NULL) { - status_t status = sBufferModule->merge(fSendBuffer, buffer, true); - if (status != B_OK) - return status; - } else - fSendBuffer = buffer; - - fNextByteToWrite += bufferSize; - return SendQueuedData(TCP_FLG_ACK, false); -} - - -status_t -TCPConnection::SendRoutedData(net_route *route, net_buffer *buffer) -{ - TRACE(("TCP:%p.SendRoutedData()\n", this)); - { - BenaphoreLocker lock(&fLock); - fRoute = route; - } - return SendData(buffer); -} - - -size_t -TCPConnection::SendAvailable() -{ - TRACE(("TCP:%p.SendAvailable()\n", this)); - BenaphoreLocker lock(&fLock); - if (fSendBuffer != NULL) - return TCP_MAX_SEND_BUF - fSendBuffer->size; - - return TCP_MAX_SEND_BUF; -} - - -status_t -TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer) -{ - TRACE(("TCP:%p.ReadData()\n", this)); - - BenaphoreLocker lock(&fLock); - - // must be in a synchronous state - if (fState != ESTABLISHED || fState != FIN_WAIT1 || fState != FIN_WAIT2) { - // is this correct semantics? -dprintf(" TCP state = %d\n", fState); - return B_ERROR; - } - -dprintf(" TCP error = %ld\n", fError); - if (fError != B_OK) - return fError; - - if (fReceiveBuffer->size < numBytes) - numBytes = fReceiveBuffer->size; - - *_buffer = sBufferModule->split(fReceiveBuffer, numBytes); - if (*_buffer == NULL) - return B_NO_MEMORY; - - return B_OK; -} - - -size_t -TCPConnection::ReadAvailable() -{ - TRACE(("TCP:%p.ReadAvailable()\n", this)); - BenaphoreLocker lock(&fLock); - if (fReceiveBuffer != NULL) - return fReceiveBuffer->size; - - return 0; -} - - -status_t -TCPConnection::EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber) -{ - TRACE(("TCP:%p.EnqueueReceivedData(%p, %lu)\n", this, buffer, sequenceNumber)); - status_t status; - - if (sequenceNumber == fNextByteExpected) { - // first check if the received buffer meets up with the first - // segment in the ReorderQueue - TCPSegment *next; - while ((next = (TCPSegment *)list_get_first_item(&fReorderQueue)) != NULL) { - if (sequenceNumber + buffer->size >= next->fSequenceNumber) { - if (sequenceNumber + buffer->size > next->fSequenceNumber) { - status = sBufferModule->trim(buffer, sequenceNumber - next->fSequenceNumber); - if (status != B_OK) - return status; - } - status = sBufferModule->merge(buffer, next->fBuffer, true); - if (status != B_OK) - return status; - list_remove_item(&fReorderQueue, next); - delete next; - } else - break; - } - - fNextByteExpected += buffer->size; - if (fReceiveBuffer == NULL) - fReceiveBuffer = buffer; - else { - status = sBufferModule->merge(fReceiveBuffer, buffer, true); - if (status < B_OK) { - fNextByteExpected -= buffer->size; - return status; - } - } - } else { - // add this buffer into the ReorderQueue in the correct place - // creating a new TCPSegment if necessary - TCPSegment *next = NULL; - do { - next = (TCPSegment *)list_get_next_item(&fReorderQueue, next); - if (next != NULL && next->fSequenceNumber < sequenceNumber) - continue; - if (next != NULL && sequenceNumber + buffer->size >= next->fSequenceNumber) { - // merge the new buffer with the next buffer - if (sequenceNumber + buffer->size > next->fSequenceNumber) { - status = sBufferModule->trim(buffer, sequenceNumber - next->fSequenceNumber); - if (status != B_OK) - return status; - } - status = sBufferModule->merge(buffer, next->fBuffer, true); - if (status != B_OK) - return status; - next->fBuffer = buffer; - next->fSequenceNumber = sequenceNumber; - break; - } - TCPSegment *segment = new(std::nothrow) TCPSegment(buffer, sequenceNumber, -1); - if (next == NULL) - list_add_item(&fReorderQueue, segment); - else - list_insert_item_before(&fReorderQueue, next, segment); - } while (next != NULL); - } - return B_OK; -} - - -status_t -TCPConnection::ReceiveData(net_buffer *buffer) -{ - BenaphoreLocker lock(&fLock); - TRACE(("TCP:%p.ReceiveData()\n", this)); - - NetBufferHeader bufferHeader(buffer); - if (bufferHeader.Status() < B_OK) - return bufferHeader.Status(); - - tcp_header &header = bufferHeader.Data(); - - uint16 flags = 0x0; - tcp_state nextState = fState; - status_t status = B_OK; - uint32 byteAckd = ntohl(header.acknowledge_num); - uint32 byteRcvd = ntohl(header.sequence_num); - uint32 headerLength = (uint32)header.header_length << 2; - uint32 payloadLength = buffer->size - headerLength; - - TRACE(("TCP: ReceiveData(): Connection in state %d received packet %p with flags %X!\n", fState, buffer, header.flags)); - switch (fState) { - case CLOSED: - case TIME_WAIT: - sBufferModule->free(buffer); - if (header.flags & TCP_FLG_ACK) - return Reset(byteAckd, 0); - - return Reset(0, byteRcvd + payloadLength); - - case LISTEN: - // if packet is SYN, spawn new TCPConnection in SYN_RCVD state - // and add it to the Connection Queue. The new TCPConnection - // must continue the handshake by replying with SYN+ACK. Any - // data in the packet must go into the new TCPConnection's receive - // buffer. - // Otherwise, RST+ACK is sent. - // The current TCPConnection always remains in LISTEN state. - return B_ERROR; - - case SYN_SENT: - if (header.flags & TCP_FLG_RST) { - fError = ECONNREFUSED; - fState = CLOSED; - return B_ERROR; - } - - if (header.flags & TCP_FLG_ACK && !TCP_IS_GOOD_ACK(byteAckd)) - return Reset(byteAckd, 0); - - if (header.flags & TCP_FLG_SYN) { - fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1; - flags |= TCP_FLG_ACK; - fLastByteAckd = byteAckd; - - // cancel resend of this segment - if (header.flags & TCP_FLG_ACK) - nextState = ESTABLISHED; - else { - nextState = SYN_RCVD; - flags |= TCP_FLG_SYN; - } - } - break; - - case SYN_RCVD: - if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd)) - fState = ESTABLISHED; - else - Reset(byteAckd, 0); - break; - - default: - // In a synchronized state. - // first check that the received sequence number is good - if (TCP_IS_GOOD_SEQ(byteRcvd, payloadLength)) { - // If a valid RST was received, terminate the connection. - if (header.flags & TCP_FLG_RST) { - fError = ECONNREFUSED; - fState = CLOSED; - return B_ERROR; - } - - if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd) ) { - fLastByteAckd = byteAckd; - if (fLastByteAckd == fNextByteToWrite) { - if (fState == LAST_ACK ) { - nextState = CLOSED; - status = hash_remove(sTCPHash, this); - if (status != B_OK) - return status; - } - if (fState == CLOSING) { - nextState = TIME_WAIT; - status = hash_remove(sTCPHash, this); - if (status != B_OK) - return status; - } - if (fState == FIN_WAIT1) { - nextState = FIN_WAIT2; - } - } - } - if (header.flags & TCP_FLG_FIN) { - // other side is closing connection. change states - switch (fState) { - case ESTABLISHED: - nextState = CLOSE_WAIT; - fNextByteExpected++; - break; - case FIN_WAIT2: - nextState = TIME_WAIT; - fNextByteExpected++; - break; - case FIN_WAIT1: - if (fLastByteAckd == fNextByteToWrite) { - // our FIN has been ACKd: go to TIME_WAIT - nextState = TIME_WAIT; - status = hash_remove(sTCPHash, this); - if (status != B_OK) - return status; - sStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME); - } else - nextState = CLOSING; - fNextByteExpected++; - break; - default: - break; - } - } - flags |= TCP_FLG_ACK; - } else { - // out-of-order packet received. remind the other side of where we are - return SendQueuedData(TCP_FLG_ACK, true); - } - break; - } - TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState)); - - // state machine is done switching states and the data is good. - // put it in the receive buffer - // TODO: This isn't the most efficient way to do it, and will need to be changed - // to deal with Silly Window Syndrome - bufferHeader.Remove(headerLength); - if (buffer->size > 0) { - status = EnqueueReceivedData(buffer, byteRcvd); - if (status != B_OK) - return status; - } else - sBufferModule->free(buffer); - - if (fState != CLOSING && fState != LAST_ACK) { - status = SendQueuedData(flags, false); - if (status != B_OK) - return status; - } - - fState = nextState; - return B_OK; -} - - -status_t -TCPConnection::Reset(uint32 sequenceNum, uint32 acknowledgeNum) -{ - TRACE(("TCP:%p.Reset()\n", this)); - net_buffer *reply_buf = sBufferModule->create(512); - sAddressModule->set_to((sockaddr *)&reply_buf->source, (sockaddr *)&socket->address); - sAddressModule->set_to((sockaddr *)&reply_buf->destination, (sockaddr *)&socket->peer); - - uint16 flags = TCP_FLG_RST | acknowledgeNum == 0 ? 0 : TCP_FLG_ACK; - status_t status = tcp_segment(reply_buf, flags , sequenceNum, acknowledgeNum, 0); - if (status != B_OK) { - sBufferModule->free(reply_buf); - return status; - } - TRACE(("TCP: Reset():Sending RST...\n")); - status = next->module->send_routed_data(next, fRoute, reply_buf); - if (status !=B_OK) - sBufferModule->free(reply_buf); - return status; -} - - -/*! - Resends a sent segment (\a data) if the segment's ACK wasn't received - before the timeout (eg \a timer expired) -*/ -void -TCPConnection::ResendSegment(struct net_timer *timer, void *data) -{ - TRACE(("TCP:ResendSegment(%p)\n", data)); - if (data == NULL) - return; -} - - -/*! - Sends a TCP packet with the specified \a flags. If there is any data in - the send buffer and \a empty is false, fEffectiveWindow bytes or less of it are sent as well. - Sequence and Acknowledgement numbers are filled in accordingly. - The fLock benaphore must be held before calling. -*/ -status_t -TCPConnection::SendQueuedData(uint16 flags, bool empty) -{ - TRACE(("TCP:%p.SendQueuedData(%X,%s)\n", this, flags, empty ? "1" : "0")); - - if (fRoute == NULL) - return B_ERROR; - - net_buffer *buffer; - uint32 effectiveWindow = min_c(tcp_get_mtu(this, (sockaddr *)&socket->address), fNextByteToWrite - fNextByteToSend); - if (empty || effectiveWindow == 0 || fSendBuffer == NULL || fSendBuffer->size == 0) { - buffer = sBufferModule->create(256); - TRACE(("TCP: Sending Buffer %p\n", buffer)); - if (buffer == NULL) - return ENOBUFS; - } else { - buffer = fSendBuffer; - if (effectiveWindow == fSendBuffer->size) - fSendBuffer = NULL; - else - fSendBuffer = sBufferModule->split(fSendBuffer, effectiveWindow); - } - - sAddressModule->set_to((sockaddr *)&buffer->source, (sockaddr *)&socket->address); - sAddressModule->set_to((sockaddr *)&buffer->destination, (sockaddr *)&socket->peer); - TRACE(("TCP:%p.SendQueuedData() to address %s\n", this, - AddressString(sDomain, (sockaddr *)&buffer->destination, true).Data())); - TRACE(("TCP:%p.SendQueuedData() from address %s\n", this, - AddressString(sDomain, (sockaddr *)&buffer->source, true).Data())); - - uint16 advWin = TCP_MAX_RECV_BUF - (fNextByteExpected - fNextByteToRead); - uint32 size = buffer->size; - - status_t status = tcp_segment(buffer, flags, fNextByteToSend, fNextByteExpected, advWin); - if (status != B_OK) { - sBufferModule->free(buffer); - return status; - } - // Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN - if (TCP_FLG_SYN & flags && (fState == CLOSED || fState == LISTEN)) - fNextByteToSend++; - // Only count 1 FIN, the 1 sent when transitioning from ESTABLISHED, SYN_RCVD or CLOSE_WAIT - if (TCP_FLG_FIN & flags && (fState == SYN_RCVD || fState == ESTABLISHED || fState == CLOSE_WAIT)) - fNextByteToSend++; - fNextByteToSend += size; - -#if 0 - TCPSegment *segment = new(std::nothrow) - TCPSegment(sequenceNum, 0, 2*fAvgRTT); -#endif - - return next->module->send_routed_data(next, fRoute, buffer); -} - - -void -TCPConnection::TimeWait(struct net_timer *timer, void *data) -{ -} - - -int -TCPConnection::Compare(void *_connection, const void *_key) -{ - const tcp_connection_key *key = (tcp_connection_key *)_key; - TCPConnection *connection= ((TCPConnection *)_connection); - - if (sAddressModule->equal_addresses_and_ports(key->local, - (sockaddr *)&connection->socket->address) - && sAddressModule->equal_addresses_and_ports(key->peer, - (sockaddr *)&connection->socket->peer)) - return 0; - - return 1; -} - - -uint32 -TCPConnection::Hash(void *_connection, const void *_key, uint32 range) -{ - if (_connection != NULL) { - TCPConnection *connection = (TCPConnection *)_connection; - return sAddressModule->hash_address_pair( - (sockaddr *)&connection->socket->address, (sockaddr *)&connection->socket->peer) % range; - } - - const tcp_connection_key *key = (tcp_connection_key *)_key; - return sAddressModule->hash_address_pair( - key->local, key->peer) % range; -} +#endif // TCP_CONNECTION_H diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index e3150ed69a..68599eb88e 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -4,13 +4,13 @@ * * Authors: * Axel Dörfler, axeld@pinc-software.de + * Andrew Galante, haiku.galante@gmail.com */ -#include -#include +#include "TCPConnection.h" + #include -#include #include #include @@ -23,7 +23,6 @@ #include #include -#include #include #include @@ -40,51 +39,112 @@ #define MAX_HASH_TCP 64 -static net_domain *sDomain; -static net_address_module_info *sAddressModule; -net_buffer_module_info *sBufferModule; -static net_datalink_module_info *sDatalinkModule; -static net_stack_module_info *sStackModule; -static hash_table *sTCPHash; -static benaphore sTCPLock; -status_t -tcp_segment(net_buffer *buffer, uint16 flags, uint32 seq, uint32 ack, uint16 adv_win); - -size_t -tcp_get_mtu(net_protocol *protocol, const struct sockaddr *address); - -#include "tcp.h" -#include "TCPConnection.h" +net_domain *gDomain; +net_address_module_info *gAddressModule; +net_buffer_module_info *gBufferModule; +net_datalink_module_info *gDatalinkModule; +net_stack_module_info *gStackModule; +hash_table *gConnectionHash; +benaphore gConnectionLock; #ifdef TRACE_TCP # define DUMP_TCP_HASH tcp_dump_hash() -// Dumps the TCP Connection hash. sTCPLock must NOT be held when calling +// Dumps the TCP Connection hash. gConnectionLock must NOT be held when calling void -tcp_dump_hash(){ - BenaphoreLocker lock(&sTCPLock); - if (sDomain == NULL) { +tcp_dump_hash() +{ + BenaphoreLocker lock(&gConnectionLock); + if (gDomain == NULL) { TRACE(("Unable to dump TCP Connections!\n")); return; } struct hash_iterator iterator; - hash_open(sTCPHash, &iterator); + hash_open(gConnectionHash, &iterator); TCPConnection *connection; - hash_rewind(sTCPHash, &iterator); + hash_rewind(gConnectionHash, &iterator); TRACE(("Active TCP Connections:\n")); - while ((connection = (TCPConnection *)hash_next(sTCPHash, &iterator)) != NULL) { + while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) { TRACE((" TCPConnection %p: %s, %s\n", connection, - AddressString(sDomain, (sockaddr *)&connection->socket->address, true).Data(), - AddressString(sDomain, (sockaddr *)&connection->socket->peer, true).Data())); + AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(), + AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data())); } - hash_close(sTCPHash, &iterator, false); + hash_close(gConnectionHash, &iterator, false); } #else # define DUMP_TCP_HASH 0 #endif +status_t +set_domain(net_interface *interface = NULL) +{ + if (gDomain == NULL) { + // domain and address module are not known yet, we copy them from + // the buffer's interface (if any): + if (interface == NULL || interface->domain == NULL) + gDomain = gStackModule->get_domain(AF_INET); + else + gDomain = interface->domain; + + if (gDomain == NULL) { + // this shouldn't occur, of course, but who knows... + return B_BAD_VALUE; + } + gAddressModule = gDomain->address_module; + } + + return B_OK; +} + + +/*! + Constructs a TCP header on \a buffer with the specified values + for \a flags, \a seq \a ack and \a advertisedWindow. +*/ +status_t +add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack, + uint16 advertisedWindow) +{ + buffer->protocol = IPPROTO_TCP; + + NetBufferPrepend bufferHeader(buffer); + if (bufferHeader.Status() != B_OK) + return bufferHeader.Status(); + + tcp_header &header = bufferHeader.Data(); + + header.source_port = gAddressModule->get_port((sockaddr *)&buffer->source); + header.destination_port = gAddressModule->get_port((sockaddr *)&buffer->destination); + header.sequence_num = htonl(sequence); + header.acknowledge_num = htonl(ack); + header.reserved = 0; + header.header_length = 5; + // currently no options supported + header.flags = (uint8)flags; + header.advertised_window = htons(advertisedWindow); + header.checksum = 0; + header.urgent_ptr = 0; + // urgent pointer not supported + + // compute and store checksum + Checksum checksum; + gAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->source); + gAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->destination); + checksum + << (uint16)htons(IPPROTO_TCP) + << (uint16)htons(buffer->size) + << Checksum::BufferHelper(buffer, gBufferModule); + header.checksum = checksum; + TRACE(("TCP: Checksum for segment %p is %X\n", buffer, header.checksum)); + return B_OK; +} + + +// #pragma mark - protocol API + + net_protocol * tcp_init_protocol(net_socket *socket) { @@ -109,10 +169,9 @@ tcp_uninit_protocol(net_protocol *protocol) status_t tcp_open(net_protocol *protocol) { - if (!sDomain) - sDomain = sStackModule->get_domain(AF_INET); - if (!sAddressModule) - sAddressModule = sDomain->address_module; + if (gDomain == NULL && set_domain() != B_OK) + return B_ERROR; + DUMP_TCP_HASH; return ((TCPConnection *)protocol)->Open(); @@ -240,63 +299,13 @@ tcp_get_mtu(net_protocol *protocol, const struct sockaddr *address) } -/*! - Constructs a TCP header on \a buffer with the specified values - for \a flags, \a seq \a ack and \a adv_win. -*/ -status_t -tcp_segment(net_buffer *buffer, uint16 flags, uint32 seq, uint32 ack, uint16 adv_win) -{ - buffer->protocol = IPPROTO_TCP; - - NetBufferPrepend bufferHeader(buffer); - if (bufferHeader.Status() != B_OK) - return bufferHeader.Status(); - - tcp_header &header = bufferHeader.Data(); - - header.source_port = sAddressModule->get_port((sockaddr *)&buffer->source); - header.destination_port = sAddressModule->get_port((sockaddr *)&buffer->destination); - header.sequence_num = htonl(seq); - header.acknowledge_num = htonl(ack); - header.reserved = 0; - header.header_length = 5;// currently no options supported - header.flags = (uint8)flags; - header.advertised_window = htons(adv_win); - header.checksum = 0; - header.urgent_ptr = 0;// urgent pointer not supported - - // compute and store checksum - Checksum checksum; - sAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->source); - sAddressModule->checksum_address(&checksum, (sockaddr *)&buffer->destination); - checksum - << (uint16)htons(IPPROTO_TCP) - << (uint16)htons(buffer->size) - << Checksum::BufferHelper(buffer, sBufferModule); - header.checksum = checksum; - TRACE(("TCP: Checksum for segment %p is %X\n", buffer, header.checksum)); - return B_OK; -} - - status_t tcp_receive_data(net_buffer *buffer) { TRACE(("TCP: Received buffer %p\n", buffer)); - if (!sDomain) { - // domain and address module are not known yet, we copy them from - // the buffer's interface (if any): - if (buffer->interface == NULL || buffer->interface->domain == NULL) - sDomain = sStackModule->get_domain(AF_INET); - else - sDomain = buffer->interface->domain; - if (sDomain == NULL) { - // this shouldn't occur, of course, but who knows... - return B_BAD_VALUE; - } - sAddressModule = sDomain->address_module; - } + + if (gDomain == NULL && set_domain(buffer->interface) != B_OK) + return B_ERROR; NetBufferHeader bufferHeader(buffer); if (bufferHeader.Status() < B_OK) @@ -308,15 +317,15 @@ tcp_receive_data(net_buffer *buffer) key.peer = (struct sockaddr *)&buffer->source; key.local = (struct sockaddr *)&buffer->destination; - //TODO: check TCP Checksum + // TODO: check TCP Checksum - sAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port); - sAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port); + gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port); + gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port); DUMP_TCP_HASH; - BenaphoreLocker hashLock(&sTCPLock); - TCPConnection *connection = (TCPConnection *)hash_lookup(sTCPHash, &key); + BenaphoreLocker hashLock(&gConnectionLock); + TCPConnection *connection = (TCPConnection *)hash_lookup(gConnectionHash, &key); TRACE(("TCP: Received packet corresponds to connection %p\n", connection)); if (connection != NULL){ return connection->ReceiveData(buffer); @@ -330,31 +339,37 @@ tcp_receive_data(net_buffer *buffer) // If no connection exists (and RST is not set) send RST if (!(header.flags & TCP_FLG_RST)) { TRACE(("TCP: Connection does not exist!\n")); - net_buffer *reply_buf = sBufferModule->create(512); - sAddressModule->set_to((sockaddr *)&reply_buf->source, (sockaddr *)&buffer->destination); - sAddressModule->set_to((sockaddr *)&reply_buf->destination, (sockaddr *)&buffer->source); + net_buffer *reply = gBufferModule->create(512); + if (reply == NULL) + return B_NO_MEMORY; - uint32 sequenceNum, acknowledgeNum; + gAddressModule->set_to((sockaddr *)&reply->source, + (sockaddr *)&buffer->destination); + gAddressModule->set_to((sockaddr *)&reply->destination, + (sockaddr *)&buffer->source); + + uint32 sequence, acknowledge; uint16 flags; if (header.flags & TCP_FLG_ACK) { - sequenceNum = ntohl(header.acknowledge_num); - acknowledgeNum = 0; + sequence = ntohl(header.acknowledge_num); + acknowledge = 0; flags = TCP_FLG_RST; } else { - sequenceNum = 0; - acknowledgeNum = ntohl(header.sequence_num) + 1 + buffer->size - ((uint32)header.header_length<<2); + sequence = 0; + acknowledge = ntohl(header.sequence_num) + 1 + + buffer->size - ((uint32)header.header_length << 2); flags = TCP_FLG_RST | TCP_FLG_ACK; } - - status_t status = tcp_segment(reply_buf, flags, sequenceNum, acknowledgeNum, 0); - if (status != B_OK) { - sBufferModule->free(reply_buf); - return status; + + status_t status = add_tcp_header(reply, flags, sequence, acknowledge, 0); + + if (status == B_OK) { + TRACE(("TCP: Sending RST...\n")); + status = gDomain->module->send_data(NULL, reply); } - TRACE(("TCP: Sending RST...\n")); - status = sDomain->module->send_data(NULL, reply_buf); - if (status !=B_OK) { - sBufferModule->free(reply_buf); + + if (status != B_OK) { + gBufferModule->free(reply); return status; } } @@ -386,43 +401,43 @@ tcp_init() { status_t status; - sDomain = NULL; - sAddressModule = NULL; + gDomain = NULL; + gAddressModule = NULL; - status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule); + status = get_module(NET_STACK_MODULE_NAME, (module_info **)&gStackModule); if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) goto err1; - status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&sDatalinkModule); + status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule); if (status < B_OK) goto err2; - sTCPHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(), + gConnectionHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(), &TCPConnection::Compare, &TCPConnection::Hash); - if (sTCPHash == NULL) + if (gConnectionHash == NULL) goto err3; - status = benaphore_init(&sTCPLock, "TCP Hash Lock"); + status = benaphore_init(&gConnectionLock, "TCP Hash Lock"); if (status < B_OK) goto err4; - status = sStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_IP, + status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_IP, "network/protocols/tcp/v1", "network/protocols/ipv4/v1", NULL); if (status < B_OK) goto err5; - status = sStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP, + status = gStackModule->register_domain_protocols(AF_INET, SOCK_STREAM, IPPROTO_TCP, "network/protocols/tcp/v1", "network/protocols/ipv4/v1", NULL); if (status < B_OK) goto err5; - status = sStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP, + status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP, "network/protocols/tcp/v1"); if (status < B_OK) goto err5; @@ -430,9 +445,9 @@ tcp_init() return B_OK; err5: - benaphore_destroy(&sTCPLock); + benaphore_destroy(&gConnectionLock); err4: - hash_uninit(sTCPHash); + hash_uninit(gConnectionHash); err3: put_module(NET_DATALINK_MODULE_NAME); err2: @@ -448,8 +463,8 @@ err1: static status_t tcp_uninit() { - benaphore_destroy(&sTCPLock); - hash_uninit(sTCPHash); + benaphore_destroy(&gConnectionLock); + hash_uninit(gConnectionHash); put_module(NET_DATALINK_MODULE_NAME); put_module(NET_BUFFER_MODULE_NAME); put_module(NET_STACK_MODULE_NAME); diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index bb73601ef8..2ccd57e050 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -5,8 +5,18 @@ * Authors: * Andrew Galante, haiku.galante@gmail.com */ +#ifndef TCP_H +#define TCP_H + + +#include +#include +#include + +#include + +#include -#include typedef enum { CLOSED, @@ -51,3 +61,23 @@ struct tcp_header { #define TCP_FLG_RST 0x04 // ReSeT #define TCP_FLG_SYN 0x02 // SYNchronize #define TCP_FLG_FIN 0x01 // FINish + +struct tcp_connection_key { + const sockaddr *local; + const sockaddr *peer; +}; + + +extern net_domain *gDomain; +extern net_address_module_info *gAddressModule; +extern net_buffer_module_info *gBufferModule; +extern net_datalink_module_info *gDatalinkModule; +extern net_stack_module_info *gStackModule; +extern hash_table *gConnectionHash; +extern benaphore gConnectionLock; + + +status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, + uint32 ack, uint16 advertisedWindow); + +#endif TCP_H diff --git a/src/add-ons/kernel/network/protocols/udp/udp.cpp b/src/add-ons/kernel/network/protocols/udp/udp.cpp index fc24af8e38..5f52e649bb 100644 --- a/src/add-ons/kernel/network/protocols/udp/udp.cpp +++ b/src/add-ons/kernel/network/protocols/udp/udp.cpp @@ -147,7 +147,7 @@ static UdpEndpointManager *sUdpEndpointManager; static net_domain *sDomain; static net_address_module_info *sAddressModule; -net_buffer_module_info *sBufferModule; +net_buffer_module_info *gBufferModule; static net_datalink_module_info *sDatalinkModule; static net_stack_module_info *sStackModule; @@ -495,7 +495,7 @@ UdpEndpointManager::ReceiveData(net_buffer *buffer) if (buffer->size > udpLength) { TRACE(("buffer %p is too long (%lu instead of %u), trimming it.\n", buffer, buffer->size, udpLength)); - sBufferModule->trim(buffer, udpLength); + gBufferModule->trim(buffer, udpLength); } if (header.udp_checksum != 0) { @@ -508,7 +508,7 @@ UdpEndpointManager::ReceiveData(net_buffer *buffer) << header.udp_length // peculiar but correct: UDP-len is used twice for checksum // (as it is already contained in udp_header) - << Checksum::BufferHelper(buffer, sBufferModule); + << Checksum::BufferHelper(buffer, gBufferModule); uint16 sum = udpChecksum; if (sum != 0) { TRACE(("buffer %p has bad checksum (%u), we drop it!\n", buffer, sum)); @@ -813,7 +813,7 @@ UdpEndpoint::SendData(net_buffer *buffer, net_route *route) << (uint16)htons(buffer->size) // peculiar but correct: UDP-len is used twice for checksum // (as it is already contained in udp_header) - << Checksum::BufferHelper(buffer, sBufferModule); + << Checksum::BufferHelper(buffer, gBufferModule); header.udp_checksum = udpChecksum; if (header.udp_checksum == 0) header.udp_checksum = 0xFFFF; @@ -851,7 +851,7 @@ UdpEndpoint::FetchData(size_t numBytes, uint32 flags, net_buffer **_buffer) if (numBytes < buffer->size) { // discard any data behind the amount requested - sBufferModule->trim(buffer, numBytes); + gBufferModule->trim(buffer, numBytes); // TODO: we should indicate MSG_TRUNC to application! } @@ -866,7 +866,7 @@ UdpEndpoint::StoreData(net_buffer *_buffer) { TRACE(("buffer %p passed to endpoint with (%s)\n", _buffer, AddressString(sDomain, (sockaddr *)&socket->address, true).Data())); - net_buffer *buffer = sBufferModule->clone(_buffer, false); + net_buffer *buffer = gBufferModule->clone(_buffer, false); if (buffer == NULL) return B_NO_MEMORY; @@ -874,7 +874,7 @@ UdpEndpoint::StoreData(net_buffer *_buffer) if (status >= B_OK) sStackModule->notify_socket(socket, B_SELECT_READ, BytesAvailable()); else - sBufferModule->free(buffer); + gBufferModule->free(buffer); return status; } @@ -1098,7 +1098,7 @@ init_udp() status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule); if (status < B_OK) return status; - status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&sBufferModule); + status = get_module(NET_BUFFER_MODULE_NAME, (module_info **)&gBufferModule); if (status < B_OK) goto err1; status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&sDatalinkModule);