axeld + bonefish:

* Added syscall restart support for connect(), accept(), send(), recv(),
  which are implemented via ioctl()s. The actual restart support is done
  in the net stack driver's ioctl() hook. Lower layers need to correctly
  deal with socket timeouts, though, for which the stack module provides
  support functions.
* TCPEndpoint::_WaitForEstablished() does abort now when an error
  occurred earlier, so that trying to connect to an unused port fails
  immediately, as it should.
* Fixed and refactored TCP connection reset handling. The new
  TCPEndpoint::_HandleReset() does the job. Got rid of
  TCPEndpoint::fError.
* Fixed sequence numbers for SYNC/FINI packets.
* The former two fix the problem that connections wouldn't be closed
  correctly and could even be reused when trying to connect again (as
  was reproducible with svnserve + svn).
* Some style cleanup in CPEndpoint.h.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23984 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-02-17 16:25:07 +00:00
parent 4048494ce4
commit 058494285a
9 changed files with 282 additions and 167 deletions
+5
View File
@@ -348,6 +348,11 @@ DECL_DATAGRAM_SOCKET(inline bigtime_t)::_SocketTimeout(uint32 flags) const
else if (timeout != 0 && timeout != B_INFINITE_TIMEOUT)
timeout += system_time();
if (ModuleBundle::Stack()->is_restarted_syscall())
timeout = ModuleBundle::Stack()->restore_syscall_restart_timeout();
else
ModuleBundle::Stack()->store_syscall_restart_timeout(timeout);
return timeout;
}
+9 -3
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef NET_STACK_H
@@ -44,8 +44,9 @@ struct net_timer {
bigtime_t due;
};
typedef int32 (*net_deframe_func)(struct net_device *device, struct net_buffer *buffer);
typedef status_t (*net_receive_func)(void *cookie, struct net_device *,
typedef int32 (*net_deframe_func)(struct net_device *device,
struct net_buffer *buffer);
typedef status_t (*net_receive_func)(void *cookie, struct net_device *device,
struct net_buffer *buffer);
enum {
@@ -132,6 +133,11 @@ struct net_stack_module_info {
void (*set_timer)(struct net_timer *timer, bigtime_t delay);
bool (*cancel_timer)(struct net_timer *timer);
bool (*is_timer_active)(struct net_timer *timer);
// syscall restart
bool (*is_restarted_syscall)(void);
void (*store_syscall_restart_timeout)(bigtime_t timeout);
bigtime_t (*restore_syscall_restart_timeout)(void);
};
#endif // NET_STACK_H
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2007, Haiku, Inc. All Rights Reserved.
* Copyright 2002-2008, Haiku, Inc. All Rights Reserved.
* This file may be used under the terms of the MIT License.
*/
@@ -10,16 +10,17 @@
#include <net_stack_driver.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <driver_settings.h>
#include <Drivers.h>
#include <KernelExport.h>
#include <driver_settings.h>
#include <kernel.h> // IS_KERNEL_ADDRESS
#include <fs/fd.h> // user_fd_kernel_ioctl
#include <fs/fd.h>
#include <kernel.h>
#include <syscall_restart.h>
#include <net_socket.h>
@@ -96,8 +97,9 @@ check_args_and_address(ArgType &args, sockaddr_storage &address, void *data, siz
static status_t
check_message_args(message_args &args, msghdr &header, sockaddr_storage &address,
void *data, size_t length, sockaddr **originalAddress)
check_message_args(message_args &args, msghdr &header,
sockaddr_storage &address, void *data, size_t length,
sockaddr **originalAddress)
{
if (length < sizeof(message_args))
return B_BAD_VALUE;
@@ -133,6 +135,7 @@ check_message_args(message_args &args, msghdr &header, sockaddr_storage &address
return B_OK;
}
template<typename ArgType> status_t
check_args(ArgType &args, void *data, size_t length)
{
@@ -205,7 +208,7 @@ resolve_cookie(int kernel, int socket, net_stack_cookie **cookie)
{
if (kernel)
return ioctl(socket, NET_STACK_GET_COOKIE, cookie, sizeof(*cookie));
else
return user_fd_kernel_ioctl(socket, NET_STACK_GET_COOKIE, cookie,
sizeof(*cookie));
}
@@ -251,7 +254,8 @@ net_stack_open(const char *name, uint32 flags, void **_cookie)
}
}
net_stack_cookie *cookie = (net_stack_cookie *)malloc(sizeof(net_stack_cookie));
net_stack_cookie *cookie = (net_stack_cookie *)malloc(
sizeof(net_stack_cookie));
if (cookie == NULL)
return B_NO_MEMORY;
@@ -363,7 +367,10 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (status < B_OK)
return status;
return sSocket->connect(cookie->socket, args.address, args.address_length);
status = sSocket->connect(cookie->socket, args.address,
args.address_length);
return syscall_restart_ioctl_handle_post(status);
}
case NET_STACK_BIND:
@@ -387,7 +394,8 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
sockaddr_storage address;
accept_args args;
status = check_args_and_address(args, address, data, length, false);
status = check_args_and_address(args, address, data, length,
false);
if (status < B_OK)
return status;
@@ -399,8 +407,9 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
status = sSocket->accept(cookie->socket, args.address,
&args.address_length, &acceptCookie->socket);
if (status < B_OK)
return status;
return syscall_restart_ioctl_handle_post(status);
return return_address(args, data);
}
@@ -419,9 +428,11 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (status < B_OK)
return status;
return sSocket->send(cookie->socket,
ssize_t bytesSent = sSocket->send(cookie->socket,
args.header ? &header : NULL,
args.data, args.length, args.flags);
return syscall_restart_ioctl_handle_post(bytesSent);
}
case NET_STACK_RECEIVE:
@@ -440,7 +451,7 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
args.header ? &header : NULL, args.data,
args.length, args.flags);
if (bytesRead < B_OK)
return bytesRead;
return syscall_restart_ioctl_handle_post(bytesRead);
if (args.header != NULL) {
if (header.msg_name != NULL) {
@@ -468,13 +479,14 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (args.length > (int)sizeof(valueBuffer))
return ENOBUFS;
status = sSocket->getsockopt(cookie->socket, args.level, args.option,
valueBuffer, &args.length);
status = sSocket->getsockopt(cookie->socket, args.level,
args.option, valueBuffer, &args.length);
if (status < B_OK)
return status;
if (user_memcpy(args.value, valueBuffer, args.length) < B_OK
|| user_memcpy(&((sockopt_args *)data)->length, &args.length, sizeof(int)) < B_OK)
|| user_memcpy(&((sockopt_args *)data)->length,
&args.length, sizeof(int)) < B_OK)
return B_BAD_ADDRESS;
return B_OK;
@@ -493,15 +505,16 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
if (user_memcpy(valueBuffer, args.value, args.length) < B_OK)
return B_BAD_ADDRESS;
return sSocket->setsockopt(cookie->socket, args.level, args.option,
valueBuffer, args.length);
return sSocket->setsockopt(cookie->socket, args.level,
args.option, valueBuffer, args.length);
}
case NET_STACK_GETSOCKNAME:
{
sockaddr_storage address;
sockaddr_args args;
status = check_args_and_address(args, address, data, length, false);
status = check_args_and_address(args, address, data, length,
false);
if (status < B_OK)
return status;
@@ -517,7 +530,8 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
{
sockaddr_storage address;
sockaddr_args args;
status = check_args_and_address(args, address, data, length, false);
status = check_args_and_address(args, address, data, length,
false);
if (status < B_OK)
return status;
@@ -532,16 +546,16 @@ net_stack_control(void *_cookie, uint32 op, void *data, size_t length)
case FIONBIO:
{
int value = (int)data;
return sSocket->setsockopt(cookie->socket, SOL_SOCKET, SO_NONBLOCK,
&value, sizeof(int));
return sSocket->setsockopt(cookie->socket, SOL_SOCKET,
SO_NONBLOCK, &value, sizeof(int));
}
case B_SET_BLOCKING_IO:
case B_SET_NONBLOCKING_IO:
{
int value = op == B_SET_NONBLOCKING_IO;
return sSocket->setsockopt(cookie->socket, SOL_SOCKET, SO_NONBLOCK,
&value, sizeof(int));
return sSocket->setsockopt(cookie->socket, SOL_SOCKET,
SO_NONBLOCK, &value, sizeof(int));
}
default:
@@ -405,6 +405,9 @@ EndpointManager::ReplyWithReset(tcp_segment_header &segment,
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) == 0) {
outSegment.flags |= TCP_FLAG_ACKNOWLEDGE;
outSegment.acknowledge = segment.sequence + buffer->size;
// TODO: Confirm:
if ((segment.flags & (TCP_FLAG_SYNCHRONIZE | TCP_FLAG_FINISH)) != 0)
outSegment.acknowledge++;
} else
outSegment.sequence = segment.acknowledge;
@@ -170,6 +170,9 @@ state_needs_finish(int32 state)
}
// #pragma mark -
WaitList::WaitList(const char *name)
{
fCondition = 0;
@@ -224,6 +227,9 @@ WaitList::Signal()
}
// #pragma mark -
TCPEndpoint::TCPEndpoint(net_socket *socket)
:
ProtocolSocket(socket),
@@ -255,8 +261,7 @@ TCPEndpoint::TCPEndpoint(net_socket *socket)
fCongestionWindow(0),
fSlowStartThreshold(0),
fState(CLOSED),
fFlags(FLAG_OPTION_WINDOW_SCALE | FLAG_OPTION_TIMESTAMP),
fError(B_OK)
fFlags(FLAG_OPTION_WINDOW_SCALE | FLAG_OPTION_TIMESTAMP)
{
//gStackModule->init_timer(&fTimer, _TimeWait, this);
@@ -394,13 +399,23 @@ TCPEndpoint::Connect(const sockaddr *address)
MutexLocker locker(fLock);
if (gStackModule->is_restarted_syscall()) {
bigtime_t timeout = gStackModule->restore_syscall_restart_timeout();
status_t status = _WaitForEstablished(locker, timeout);
TRACE(" Connect(): Connection complete: %s (timeout was %llu)",
strerror(status), timeout);
return posix_error(status);
}
// Can only call connect() from CLOSED or LISTEN states
// otherwise endpoint is considered already connected
if (fState == LISTEN) {
// this socket is about to connect; remove pending connections in the backlog
gSocketModule->set_max_backlog(socket, 0);
} else if (fState != CLOSED)
} else if (fState == ESTABLISHED) {
return EISCONN;
} else if (fState != CLOSED)
return EINPROGRESS;
status_t status = _PrepareSendPath(address);
if (status < B_OK)
@@ -432,7 +447,10 @@ TCPEndpoint::Connect(const sockaddr *address)
return EINPROGRESS;
}
status = _WaitForEstablished(locker, absolute_timeout(timeout));
bigtime_t absoluteTimeout = absolute_timeout(timeout);
gStackModule->store_syscall_restart_timeout(absoluteTimeout);
status = _WaitForEstablished(locker, absoluteTimeout);
TRACE(" Connect(): Connection complete: %s (timeout was %llu)",
strerror(status), timeout);
return posix_error(status);
@@ -448,6 +466,10 @@ TCPEndpoint::Accept(struct net_socket **_acceptedSocket)
status_t status;
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
if (gStackModule->is_restarted_syscall())
timeout = gStackModule->restore_syscall_restart_timeout();
else
gStackModule->store_syscall_restart_timeout(timeout);
do {
locker.Unlock();
@@ -567,6 +589,10 @@ TCPEndpoint::SendData(net_buffer *buffer)
return ENOBUFS;
bigtime_t timeout = absolute_timeout(socket->send.timeout);
if (gStackModule->is_restarted_syscall())
timeout = gStackModule->restore_syscall_restart_timeout();
else
gStackModule->store_syscall_restart_timeout(timeout);
while (fSendQueue.Free() < buffer->size) {
status_t status = fSendList.Wait(lock, timeout);
@@ -632,6 +658,10 @@ TCPEndpoint::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
return ENOTCONN;
bigtime_t timeout = absolute_timeout(socket->receive.timeout);
if (gStackModule->is_restarted_syscall())
timeout = gStackModule->restore_syscall_restart_timeout();
else
gStackModule->store_syscall_restart_timeout(timeout);
if (fState == SYNCHRONIZE_SENT || fState == SYNCHRONIZE_RECEIVED) {
if (flags & MSG_DONTWAIT)
@@ -913,11 +943,27 @@ TCPEndpoint::DumpInternalState() const
}
void
TCPEndpoint::_HandleReset(status_t error)
{
gStackModule->cancel_timer(&fRetransmitTimer);
socket->error = error;
fState = CLOSED;
fSendList.Signal();
_NotifyReader();
gSocketModule->notify(socket, B_SELECT_WRITE, error);
gSocketModule->notify(socket, B_SELECT_ERROR, error);
}
int32
TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment,
net_buffer *buffer)
{
TRACE("SynchronizeSentReceive()");
TRACE("_SynchronizeSentReceive()");
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
&& (fInitialSendSequence >= segment.acknowledge
@@ -925,8 +971,7 @@ TCPEndpoint::_SynchronizeSentReceive(tcp_segment_header &segment,
return DROP | RESET;
if (segment.flags & TCP_FLAG_RESET) {
fError = ECONNREFUSED;
fState = CLOSED;
_HandleReset(ECONNREFUSED);
return DROP;
}
@@ -1040,8 +1085,10 @@ TCPEndpoint::_SegmentReceived(tcp_segment_header &segment, net_buffer *buffer)
fReceiveWindow)) {
TRACE(" Receive(): segment out of window, next: %lu wnd: %lu",
(uint32)fReceiveNext, fReceiveWindow);
if (segment.flags & TCP_FLAG_RESET)
if (segment.flags & TCP_FLAG_RESET) {
// TODO: this doesn't look right - review!
return DROP;
}
return DROP | IMMEDIATE_ACKNOWLEDGE;
}
}
@@ -1079,7 +1126,7 @@ TCPEndpoint::_CurrentFlags()
return TCP_FLAG_ACKNOWLEDGE;
default:
return B_ERROR;
return 0;
}
}
@@ -1400,16 +1447,16 @@ TCPEndpoint::_Receive(tcp_segment_header &segment, net_buffer *buffer)
if (fLastAcknowledgeSent <= segment.sequence
&& tcp_sequence(segment.sequence) < (fLastAcknowledgeSent
+ fReceiveWindow)) {
status_t error;
if (fState == SYNCHRONIZE_RECEIVED)
fError = ECONNREFUSED;
error = ECONNREFUSED;
else if (fState == CLOSING || fState == TIME_WAIT
|| fState == WAIT_FOR_FINISH_ACKNOWLEDGE)
fError = ENOTCONN;
error = ENOTCONN;
else
fError = ECONNRESET;
error = ECONNRESET;
_NotifyReader();
fState = CLOSED;
_HandleReset(error);
}
return DROP;
@@ -1635,6 +1682,9 @@ status_t
TCPEndpoint::_WaitForEstablished(MutexLocker &locker, bigtime_t timeout)
{
while (fState != ESTABLISHED) {
if (socket->error != B_OK)
return socket->error;
status_t status = fSendList.Wait(locker, timeout);
if (status < B_OK)
return status;
@@ -1752,7 +1802,7 @@ TCPEndpoint::_Acknowledged(tcp_segment_header &segment)
if (segment.options & TCP_HAS_TIMESTAMPS)
_UpdateSRTT(tcp_diff_timestamp(segment.timestamp_reply));
else {
// TODO Fallback to RFC 793 type estimation
// TODO: Fallback to RFC 793 type estimation
}
if (is_writable(fState)) {
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -42,7 +42,7 @@ private:
class TCPEndpoint : public net_protocol, public ProtocolSocket {
public:
public:
TCPEndpoint(net_socket *socket);
~TCPEndpoint();
@@ -82,7 +82,7 @@ class TCPEndpoint : public net_protocol, public ProtocolSocket {
void DumpInternalState() const;
private:
private:
friend class EndpointManager;
void _StartPersistTimer();
@@ -97,6 +97,7 @@ class TCPEndpoint : public net_protocol, public ProtocolSocket {
ssize_t _AvailableData() const;
void _NotifyReader();
bool _ShouldReceive() const;
void _HandleReset(status_t error);
int32 _ListenReceive(tcp_segment_header& segment, net_buffer *buffer);
int32 _SynchronizeSentReceive(tcp_segment_header& segment,
net_buffer *buffer);
@@ -170,7 +171,6 @@ class TCPEndpoint : public net_protocol, public ProtocolSocket {
tcp_state fState;
uint32 fFlags;
status_t fError;
// timer
net_timer fRetransmitTimer;
+5 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -932,6 +932,10 @@ net_stack_module_info gNetStackModule = {
set_timer,
cancel_timer,
is_timer_active,
is_restarted_syscall,
store_syscall_restart_timeout,
restore_syscall_restart_timeout,
};
static module_info sNetStarterModule = {
+29 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006-2007, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -11,6 +11,7 @@
#include "utility.h"
#include <net_buffer.h>
#include <syscall_restart.h>
#include <util/AutoLock.h>
#include <ByteOrder.h>
@@ -570,3 +571,30 @@ uninit_timers(void)
wait_for_thread(sTimerThread, &status);
}
// #pragma mark - Syscall restart
bool
is_restarted_syscall(void)
{
return syscall_restart_ioctl_is_restarted();
}
void
store_syscall_restart_timeout(bigtime_t timeout)
{
struct thread* thread = thread_get_current_thread();
if ((thread->flags & THREAD_FLAGS_IOCTL_SYSCALL) != 0)
*(bigtime_t*)thread->syscall_restart.parameters = timeout;
}
bigtime_t
restore_syscall_restart_timeout(void)
{
struct thread* thread = thread_get_current_thread();
return *(bigtime_t*)thread->syscall_restart.parameters;
}
+6 -1
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2006, Haiku, Inc. All Rights Reserved.
* Copyright 2006-2008, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@@ -101,4 +101,9 @@ bool is_timer_active(net_timer *timer);
status_t init_timers(void);
void uninit_timers(void);
// syscall restart
bool is_restarted_syscall(void);
void store_syscall_restart_timeout(bigtime_t timeout);
bigtime_t restore_syscall_restart_timeout(void);
#endif // NET_UTILITY_H