* 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
This commit is contained in:
@@ -54,6 +54,12 @@
|
|||||||
// Estimate for Maximum segment lifetime in the internet
|
// Estimate for Maximum segment lifetime in the internet
|
||||||
#define TCP_MAX_SEGMENT_LIFETIME (2 * TCP_INITIAL_RTT)
|
#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)
|
TCPConnection::TCPConnection(net_socket *socket)
|
||||||
:
|
:
|
||||||
@@ -76,6 +82,7 @@ TCPConnection::TCPConnection(net_socket *socket)
|
|||||||
fReceiveQueue(socket->receive.buffer_size),
|
fReceiveQueue(socket->receive.buffer_size),
|
||||||
fRoundTripTime(TCP_INITIAL_RTT),
|
fRoundTripTime(TCP_INITIAL_RTT),
|
||||||
fState(CLOSED),
|
fState(CLOSED),
|
||||||
|
fFlags(0), //FLAG_OPTION_WINDOW_SHIFT),
|
||||||
fError(B_OK)
|
fError(B_OK)
|
||||||
{
|
{
|
||||||
//gStackModule->init_timer(&fTimer, _TimeWait, this);
|
//gStackModule->init_timer(&fTimer, _TimeWait, this);
|
||||||
@@ -384,22 +391,59 @@ TCPConnection::SendData(net_buffer *buffer)
|
|||||||
{
|
{
|
||||||
TRACE(("TCP:%p.SendData()\n", this));
|
TRACE(("TCP:%p.SendData()\n", this));
|
||||||
|
|
||||||
// TODO: hangs if the buffer size is larger than the maximum!
|
size_t bytesLeft = buffer->size;
|
||||||
while (fSendQueue.Free() < buffer->size) {
|
bigtime_t timeout = socket->send.timeout;
|
||||||
status_t status = acquire_sem_etc(fSendLock, 1,
|
|
||||||
B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 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)
|
if (status < B_OK)
|
||||||
return status;
|
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;
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
RecursiveLocker locker(fLock);
|
|
||||||
fSendQueue.Add(buffer);
|
|
||||||
return _SendQueued();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -554,9 +598,22 @@ TCPConnection::ListenReceive(tcp_segment_header &segment, net_buffer *buffer)
|
|||||||
connection->fSendUnacknowledged = connection->fSendNext;
|
connection->fSendUnacknowledged = connection->fSendNext;
|
||||||
connection->fSendMax = connection->fSendNext;
|
connection->fSendMax = connection->fSendNext;
|
||||||
|
|
||||||
if (segment.max_segment_size > 0)
|
// set options
|
||||||
connection->fSendMaxSegmentSize = segment.max_segment_size;
|
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);
|
//benaphore_lock(&connection->fSendLock);
|
||||||
status_t status = connection->_SendQueued();
|
status_t status = connection->_SendQueued();
|
||||||
//benaphore_unlock(&connection->fSendLock);
|
//benaphore_unlock(&connection->fSendLock);
|
||||||
@@ -599,6 +656,18 @@ TCPConnection::SynchronizeSentReceive(tcp_segment_header &segment, net_buffer *b
|
|||||||
fReceiveNext = segment.sequence;
|
fReceiveNext = segment.sequence;
|
||||||
fReceiveQueue.SetInitialSequence(fReceiveNext);
|
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) {
|
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
|
||||||
// the connection has been established
|
// the connection has been established
|
||||||
fState = ESTABLISHED;
|
fState = ESTABLISHED;
|
||||||
@@ -1042,10 +1111,14 @@ dprintf("length = %ld, segmentLength = %lu\n", length, segmentLength);
|
|||||||
|
|
||||||
segment.sequence = fSendNext;
|
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
|
// add connection establishment options
|
||||||
segment.max_segment_size = fReceiveMaxSegmentSize;
|
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);
|
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
|
// 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++;
|
size++;
|
||||||
|
}
|
||||||
|
|
||||||
// Only count 1 FIN, the 1 sent when transitioning from
|
// Only count 1 FIN, the 1 sent when transitioning from
|
||||||
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
|
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ class TCPConnection : public net_protocol {
|
|||||||
uint32 fSlowStartThreshold;
|
uint32 fSlowStartThreshold;
|
||||||
|
|
||||||
tcp_state fState;
|
tcp_state fState;
|
||||||
|
uint32 fFlags;
|
||||||
status_t fError;
|
status_t fError;
|
||||||
|
|
||||||
// timer
|
// timer
|
||||||
|
|||||||
@@ -103,8 +103,13 @@ set_domain(net_interface *interface = NULL)
|
|||||||
static inline void
|
static inline void
|
||||||
bump_option(tcp_option *&option, size_t &length)
|
bump_option(tcp_option *&option, size_t &length)
|
||||||
{
|
{
|
||||||
length += option->length;
|
if (option->kind <= TCP_OPTION_NOP) {
|
||||||
option = (tcp_option *)((uint8 *)option + option->length);
|
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);
|
option->max_segment_size = htons(segment.max_segment_size);
|
||||||
bump_option(option, length);
|
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->kind = TCP_OPTION_WINDOW_SHIFT;
|
||||||
option->length = 3;
|
option->length = 3;
|
||||||
option->window_shift = segment.window_shift;
|
option->window_shift = segment.window_shift;
|
||||||
@@ -219,6 +228,7 @@ process_options(tcp_segment_header &segment, net_buffer *buffer, int32 size)
|
|||||||
length = 4;
|
length = 4;
|
||||||
break;
|
break;
|
||||||
case TCP_OPTION_WINDOW_SHIFT:
|
case TCP_OPTION_WINDOW_SHIFT:
|
||||||
|
segment.has_window_shift = true;
|
||||||
segment.window_shift = option->window_shift;
|
segment.window_shift = option->window_shift;
|
||||||
length = 3;
|
length = 3;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -117,17 +117,17 @@ struct tcp_option {
|
|||||||
} _PACKED;
|
} _PACKED;
|
||||||
|
|
||||||
enum tcp_option_kind {
|
enum tcp_option_kind {
|
||||||
TCP_OPTION_END = 0,
|
TCP_OPTION_END = 0,
|
||||||
TCP_OPTION_NOP = 1,
|
TCP_OPTION_NOP = 1,
|
||||||
TCP_OPTION_MAX_SEGMENT_SIZE = 2,
|
TCP_OPTION_MAX_SEGMENT_SIZE = 2,
|
||||||
TCP_OPTION_WINDOW_SHIFT = 3,
|
TCP_OPTION_WINDOW_SHIFT = 3,
|
||||||
TCP_OPTION_TIMESTAMP = 8,
|
TCP_OPTION_TIMESTAMP = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TCP_MAX_WINDOW_SHIFT 14
|
#define TCP_MAX_WINDOW_SHIFT 14
|
||||||
|
|
||||||
struct tcp_segment_header {
|
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
|
// constructor zeros options
|
||||||
|
|
||||||
uint32 sequence;
|
uint32 sequence;
|
||||||
@@ -135,7 +135,8 @@ struct tcp_segment_header {
|
|||||||
uint16 advertised_window;
|
uint16 advertised_window;
|
||||||
uint16 urgent_offset;
|
uint16 urgent_offset;
|
||||||
uint8 flags;
|
uint8 flags;
|
||||||
uint8 window_shift;
|
uint8 has_window_shift : 1;
|
||||||
|
uint8 window_shift : 7;
|
||||||
uint16 max_segment_size;
|
uint16 max_segment_size;
|
||||||
|
|
||||||
bool AcknowledgeOnly() const
|
bool AcknowledgeOnly() const
|
||||||
|
|||||||
Reference in New Issue
Block a user