From 78e96e0cfef40fd01d5ca4663c8e1cebba82ba5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Fri, 1 Dec 2006 10:18:49 +0000 Subject: [PATCH] * If you send more than the maximum buffer size at once, TCP will now block until it could send all the data. * The window shift option can now also be send if the actual option is zero to indicate we support this option. * If the window shift is not specified, we'll fall back to 0 for our receive window as well now. * _SendQueued() now resets the segment options after SYN. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19407 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../network/protocols/tcp/TCPConnection.cpp | 110 +++++++++++++++--- .../network/protocols/tcp/TCPConnection.h | 1 + .../kernel/network/protocols/tcp/tcp.cpp | 16 ++- .../kernel/network/protocols/tcp/tcp.h | 15 +-- 4 files changed, 115 insertions(+), 27 deletions(-) diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp index 60e32dcb5f..403801ebea 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.cpp @@ -54,6 +54,12 @@ // Estimate for Maximum segment lifetime in the internet #define TCP_MAX_SEGMENT_LIFETIME (2 * TCP_INITIAL_RTT) +// constants for the fFlags field +enum { + FLAG_OPTION_WINDOW_SHIFT = 0x01, + FLAG_OPTION_TIMESTAMP = 0x02, +}; + TCPConnection::TCPConnection(net_socket *socket) : @@ -76,6 +82,7 @@ TCPConnection::TCPConnection(net_socket *socket) fReceiveQueue(socket->receive.buffer_size), fRoundTripTime(TCP_INITIAL_RTT), fState(CLOSED), + fFlags(0), //FLAG_OPTION_WINDOW_SHIFT), fError(B_OK) { //gStackModule->init_timer(&fTimer, _TimeWait, this); @@ -384,22 +391,59 @@ TCPConnection::SendData(net_buffer *buffer) { TRACE(("TCP:%p.SendData()\n", this)); - // TODO: hangs if the buffer size is larger than the maximum! - while (fSendQueue.Free() < buffer->size) { - status_t status = acquire_sem_etc(fSendLock, 1, - B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, socket->send.timeout); + size_t bytesLeft = buffer->size; + bigtime_t timeout = socket->send.timeout; + + do { + net_buffer *chunk; + if (bytesLeft > socket->send.buffer_size) { + // divide the buffer in multiple of the maximum segment size + size_t chunkSize = ((socket->send.buffer_size >> 1) / fSendMaxSegmentSize) + * fSendMaxSegmentSize; + + chunk = gBufferModule->split(buffer, chunkSize); + if (chunk == NULL) + return B_NO_MEMORY; + } else + chunk = buffer; + + recursive_lock_lock(&fLock); + + while (fSendQueue.Free() < chunk->size) { + recursive_lock_unlock(&fLock); + bigtime_t now = system_time(); + + status_t status = acquire_sem_etc(fSendLock, 1, + B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, timeout); + if (status < B_OK) + return status; + + if (timeout > 0) + timeout -= system_time() - now; + recursive_lock_lock(&fLock); + } + + // TODO: check state! + + if (chunk->size == 0) { + gBufferModule->free(chunk); + return B_OK; + } + + fSendQueue.Add(chunk); + status_t status = _SendQueued(); + + recursive_lock_unlock(&fLock); + if (status < B_OK) return status; - } + if (timeout < 0) + return B_WOULD_BLOCK; - // TODO: check state! + bytesLeft -= chunk->size; + } while (bytesLeft > 0); - if (buffer->size == 0) - return B_OK; - - RecursiveLocker locker(fLock); - fSendQueue.Add(buffer); - return _SendQueued(); + return B_OK; } @@ -554,9 +598,22 @@ TCPConnection::ListenReceive(tcp_segment_header &segment, net_buffer *buffer) connection->fSendUnacknowledged = connection->fSendNext; connection->fSendMax = connection->fSendNext; - if (segment.max_segment_size > 0) - connection->fSendMaxSegmentSize = segment.max_segment_size; + // set options + if ((fOptions & TCP_NOOPT) == 0) { + if (segment.max_segment_size > 0) + connection->fSendMaxSegmentSize = segment.max_segment_size; + else + connection->fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE; + if (segment.has_window_shift) { + connection->fFlags |= FLAG_OPTION_WINDOW_SHIFT; + connection->fSendWindowShift = segment.window_shift; + } else { + connection->fFlags &= ~FLAG_OPTION_WINDOW_SHIFT; + connection->fReceiveWindowShift = 0; + } + } + // send SYN+ACK //benaphore_lock(&connection->fSendLock); status_t status = connection->_SendQueued(); //benaphore_unlock(&connection->fSendLock); @@ -599,6 +656,18 @@ TCPConnection::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *b fReceiveNext = segment.sequence; fReceiveQueue.SetInitialSequence(fReceiveNext); + if (segment.max_segment_size > 0) + fSendMaxSegmentSize = segment.max_segment_size; + else + fReceiveMaxSegmentSize = TCP_DEFAULT_MAX_SEGMENT_SIZE; + if (segment.has_window_shift) { + fFlags |= FLAG_OPTION_WINDOW_SHIFT; + fSendWindowShift = segment.window_shift; + } else { + fFlags &= ~FLAG_OPTION_WINDOW_SHIFT; + fReceiveWindowShift = 0; + } + if (segment.flags & TCP_FLAG_ACKNOWLEDGE) { // the connection has been established fState = ESTABLISHED; @@ -1042,10 +1111,14 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength); segment.sequence = fSendNext; - if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) { + if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0 + && (fOptions & TCP_NOOPT) == 0) { // add connection establishment options segment.max_segment_size = fReceiveMaxSegmentSize; - segment.window_shift = fReceiveWindowShift; + if ((fFlags & FLAG_OPTION_WINDOW_SHIFT) != 0) { + segment.has_window_shift = true; + segment.window_shift = fReceiveWindowShift; + } } status = add_tcp_header(segment, buffer); @@ -1061,8 +1134,11 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength); } // Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN - if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) + if ((segment.flags & TCP_FLAG_SYNCHRONIZE) != 0) { + segment.max_segment_size = 0; + segment.has_window_shift = false; size++; + } // Only count 1 FIN, the 1 sent when transitioning from // ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED diff --git a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h index 6141f71dcd..7501d8840e 100644 --- a/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h +++ b/src/add-ons/kernel/network/protocols/tcp/TCPConnection.h @@ -114,6 +114,7 @@ class TCPConnection : public net_protocol { uint32 fSlowStartThreshold; tcp_state fState; + uint32 fFlags; status_t fError; // timer diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp index 590ab68a37..0d9557be71 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.cpp +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.cpp @@ -103,8 +103,13 @@ set_domain(net_interface *interface = NULL) static inline void bump_option(tcp_option *&option, size_t &length) { - length += option->length; - option = (tcp_option *)((uint8 *)option + option->length); + if (option->kind <= TCP_OPTION_NOP) { + length++; + option = (tcp_option *)((uint8 *)option + 1); + } else { + length += option->length; + option = (tcp_option *)((uint8 *)option + option->length); + } } @@ -120,7 +125,11 @@ add_options(tcp_segment_header &segment, uint8 *buffer, size_t bufferSize) option->max_segment_size = htons(segment.max_segment_size); bump_option(option, length); } - if (segment.window_shift > 0 && length + 4 < bufferSize) { + if (segment.has_window_shift && length + 4 < bufferSize) { + // insert one NOP so that the subsequent data is aligned on a 4 byte boundary + option->kind = TCP_OPTION_NOP; + bump_option(option, length); + option->kind = TCP_OPTION_WINDOW_SHIFT; option->length = 3; option->window_shift = segment.window_shift; @@ -219,6 +228,7 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size) length = 4; break; case TCP_OPTION_WINDOW_SHIFT: + segment.has_window_shift = true; segment.window_shift = option->window_shift; length = 3; break; diff --git a/src/add-ons/kernel/network/protocols/tcp/tcp.h b/src/add-ons/kernel/network/protocols/tcp/tcp.h index 32a3471d74..26b2b6cfc9 100644 --- a/src/add-ons/kernel/network/protocols/tcp/tcp.h +++ b/src/add-ons/kernel/network/protocols/tcp/tcp.h @@ -117,17 +117,17 @@ struct tcp_option { } _PACKED; enum tcp_option_kind { - TCP_OPTION_END = 0, - TCP_OPTION_NOP = 1, - TCP_OPTION_MAX_SEGMENT_SIZE = 2, - TCP_OPTION_WINDOW_SHIFT = 3, - TCP_OPTION_TIMESTAMP = 8, + TCP_OPTION_END = 0, + TCP_OPTION_NOP = 1, + TCP_OPTION_MAX_SEGMENT_SIZE = 2, + TCP_OPTION_WINDOW_SHIFT = 3, + TCP_OPTION_TIMESTAMP = 8, }; #define TCP_MAX_WINDOW_SHIFT 14 struct tcp_segment_header { - tcp_segment_header() : window_shift(0), max_segment_size(0) {} + tcp_segment_header() : has_window_shift(false), window_shift(0), max_segment_size(0) {} // constructor zeros options uint32 sequence; @@ -135,7 +135,8 @@ struct tcp_segment_header { uint16 advertised_window; uint16 urgent_offset; uint8 flags; - uint8 window_shift; + uint8 has_window_shift : 1; + uint8 window_shift : 7; uint16 max_segment_size; bool AcknowledgeOnly() const