TCP Socket creation and destruction implemented, as well as the beginning of the TCP state-machine

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18558 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Andrew Galante
2006-08-21 23:16:22 +00:00
parent 8ecdb90192
commit 171ee97271
2 changed files with 289 additions and 39 deletions
@@ -6,12 +6,16 @@
* Andrew Galante, [email protected]
*/
// Initial estimate for packet round trip time (RTT)
#define TCP_INITIAL_RTT 120000000LL
// 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
typedef struct {
in_addr_t source;
in_addr_t destination;
uint16 source_port;
uint16 destination_port;
const sockaddr *local;
const sockaddr *peer;
} tcp_connection_key;
@@ -35,20 +39,71 @@ public:
status_t ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer);
status_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:
TCPSegment(net_buffer *_buffer, uint32 _sequenceNumber, bigtime_t timeout);
~TCPSegment();
net_buffer *fBuffer;
bigtime_t fTime;
uint32 fSequenceNumber;
uint32 fAcknowledgementNumber;
net_timer fTimeout;
};
status_t Send(uint8 flags);
uint32 fLastByteSent;
uint32 fLastByteAckd;
uint32 fLastByteWritten;
uint32 fLastByteRead;
uint32 fNextByteExpected;
uint32 fLastByteReceived;
uint32 fEffectiveWindow;
bigtime_t fAvgRTT;
TCPSegment *fSent;
TCPSegment *fReceived;
TCPConnection *fHashLink;
tcp_state fState;
benaphore fLock;
public:
tcp_connection_key fKey;
};
TCPConnection::TCPSegment::TCPSegment(net_buffer *_buffer, uint32 _sequenceNumber, bigtime_t timeout)
:
fBuffer(_buffer),
fSequenceNumber(_sequenceNumber)
{
sStackModule->init_timer(&fTimeout, &TCPConnection::ResendSegment, this);
}
TCPConnection::TCPSegment::~TCPSegment()
{
sStackModule->set_timer(&fTimeout, -1);
sBufferModule->free(fBuffer);
}
TCPConnection::TCPConnection(net_socket *socket)
:
fLastByteSent(0),//system_time()),
fLastByteAckd(0),//fLastByteSent),
fLastByteWritten(0),//fLastByteSent),
fAvgRTT(TCP_INITIAL_RTT),
fState(CLOSED)
{
benaphore_init(&fLock, "TCPConnection");
@@ -64,6 +119,16 @@ TCPConnection::~TCPConnection()
status_t
TCPConnection::Open()
{
TRACE(("%p.Open()\n", this));
if (sAddressModule == NULL)
return B_ERROR;
TRACE(("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;
}
@@ -71,6 +136,16 @@ TCPConnection::Open()
status_t
TCPConnection::Close()
{
TRACE(("%p.Close()\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;
}
@@ -78,13 +153,75 @@ TCPConnection::Close()
status_t
TCPConnection::Free()
{
TRACE(("%p.Free()\n", 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));
// 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;
// update the hash with the new key values
key.peer = (sockaddr *)&socket->peer;
if (hash_lookup(sTCPHash, &key) != NULL) {
status = hash_remove(sTCPHash, (void *)this);
if (status != B_OK) {
TRACE(("TCP: Hash Error: %s\n", strerror(status)));
benaphore_unlock(&sTCPLock);
return status;
}
} else
TRACE(("TCP: Connect(): Connection not in hash yet!\n"));
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 = Send(TCP_FLG_SYN);
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;
}
@@ -92,16 +229,21 @@ TCPConnection::Connect(const struct sockaddr *address)
status_t
TCPConnection::Accept(struct net_socket **_acceptedSocket)
{
return B_OK;
TRACE(("%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:
@@ -109,11 +251,20 @@ TCPConnection::Bind(sockaddr *address)
if (status < B_OK)
return status;
//TODO: clean this up
/* fKey.source = ((struct sockaddr_in *)address)->sin_addr;
fKey.source_port = sAddressModule->get_port(address);
fKey.destination = INADDR_ANY;
fKey.destination_port = 0;*/
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;
}
@@ -122,6 +273,17 @@ TCPConnection::Bind(sockaddr *address)
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;
}
@@ -129,20 +291,23 @@ TCPConnection::Unbind(struct sockaddr *address)
status_t
TCPConnection::Listen(int count)
{
return B_OK;
TRACE(("%p.Listen()\n", this));
return B_ERROR;
}
status_t
TCPConnection::Shutdown(int direction)
{
return B_OK;
TRACE(("%p.Shutdown()\n", this));
return B_ERROR;
}
status_t
TCPConnection::SendData(net_buffer *buffer)
{
TRACE(("%p.SendData()\n", this));
return B_ERROR;
}
@@ -150,6 +315,7 @@ TCPConnection::SendData(net_buffer *buffer)
status_t
TCPConnection::SendRoutedData(net_route *route, net_buffer *buffer)
{
TRACE(("%p.SendRoutedData()\n", this));
return B_ERROR;
}
@@ -157,6 +323,7 @@ TCPConnection::SendRoutedData(net_route *route, net_buffer *buffer)
status_t
TCPConnection::SendAvailable()
{
TRACE(("%p.SendAvailable()\n", this));
return B_ERROR;
}
@@ -164,6 +331,7 @@ TCPConnection::SendAvailable()
status_t
TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer)
{
TRACE(("%p.ReadData()\n", this));
return B_ERROR;
}
@@ -171,20 +339,54 @@ TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer **_buffer)
status_t
TCPConnection::ReadAvailable()
{
TRACE(("%p.ReadAvailable()\n", this));
return B_ERROR;
}
status_t
TCPConnection::ReceiveData(net_buffer *buffer)
{
TRACE(("%p.ReceiveData()\n", this));
return B_ERROR;
}
/*!
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(("ResendSegment(%p)\n", data));
}
/*!
Sends a TCP packet with the specified \a flags. If there is any data in
the send buffer, 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::Send(uint8 flags)
{
TRACE(("%p.Send()\n", this));
return B_OK;
}
int
TCPConnection::Compare(void *_packet, const void *_key)
TCPConnection::Compare(void *_connection, const void *_key)
{
const tcp_connection_key *key = (tcp_connection_key *)_key;
tcp_connection_key *packetKey = &((TCPConnection *)_packet)->fKey;
TCPConnection *connection= ((TCPConnection *)_connection);
if (key->source == packetKey->source
&& key->destination == packetKey->destination
&& key->source_port == packetKey->source_port
&& key->destination_port == packetKey->destination_port)
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;
@@ -192,12 +394,15 @@ TCPConnection::Compare(void *_packet, const void *_key)
uint32
TCPConnection::Hash(void *_packet, const void *_key, uint32 range)
TCPConnection::Hash(void *_connection, const void *_key, uint32 range)
{
const tcp_connection_key *key = (tcp_connection_key *)_key;
if (_packet != NULL)
key = &((TCPConnection *)_packet)->fKey;
if (_connection != NULL) {
TCPConnection *connection = (TCPConnection *)_connection;
return sAddressModule->hash_address_pair(
(sockaddr *)&connection->socket->address, (sockaddr *)&connection->socket->peer) % range;
}
return ((uint32)key->source_port << 16 ^ (uint32)key->destination_port
^ key->source ^ key->destination) % range;
const tcp_connection_key *key = (tcp_connection_key *)_key;
return sAddressModule->hash_address_pair(
key->local, key->peer) % range;
}
@@ -48,19 +48,45 @@ static net_stack_module_info *sStackModule;
static hash_table *sTCPHash;
static benaphore sTCPLock;
#include "tcp.h"
#include "TCPConnection.h"
#ifdef TRACE_TCP
# define DUMP_HASH tcp_dump_hash()
// Dumps the TCP Connection hash. sTCPLock must NOT be held when calling
void
tcp_dump_hash(){
BenaphoreLocker lock(&sTCPLock);
if (sDomain == NULL) {
TRACE(("Unable to dump TCP Connections!\n"));
return;
}
struct hash_iterator iterator;
hash_open(sTCPHash, &iterator);
TCPConnection *connection;
hash_rewind(sTCPHash, &iterator);
TRACE(("Active TCP Connections:\n"));
while ((connection = (TCPConnection *)hash_next(sTCPHash, &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()));
}
hash_close(sTCPHash, &iterator, false);
}
#else
# define DUMP_HASH
#endif
net_protocol *
tcp_init_protocol(net_socket *socket)
{
DUMP_HASH;
socket->protocol = IPPROTO_TCP;
TCPConnection *protocol = new (std::nothrow) TCPConnection(socket);
if (protocol == NULL)
return NULL;
TRACE(("Created new TCPConnection %p\n", protocol));
TRACE(("Creating new TCPConnection: %p\n", protocol));
return protocol;
}
@@ -68,6 +94,8 @@ tcp_init_protocol(net_socket *socket)
status_t
tcp_uninit_protocol(net_protocol *protocol)
{
DUMP_HASH;
TRACE(("Deleting TCPConnection: %p\n", protocol));
delete (TCPConnection *)protocol;
return B_OK;
}
@@ -76,6 +104,12 @@ 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;
DUMP_HASH;
return ((TCPConnection *)protocol)->Open();
}
@@ -83,6 +117,7 @@ tcp_open(net_protocol *protocol)
status_t
tcp_close(net_protocol *protocol)
{
DUMP_HASH;
return ((TCPConnection *)protocol)->Close();
}
@@ -90,6 +125,7 @@ tcp_close(net_protocol *protocol)
status_t
tcp_free(net_protocol *protocol)
{
DUMP_HASH;
return ((TCPConnection *)protocol)->Free();
}
@@ -97,6 +133,7 @@ tcp_free(net_protocol *protocol)
status_t
tcp_connect(net_protocol *protocol, const struct sockaddr *address)
{
DUMP_HASH;
return ((TCPConnection *)protocol)->Connect(address);
}
@@ -120,16 +157,15 @@ tcp_control(net_protocol *protocol, int level, int option, void *value,
status_t
tcp_bind(net_protocol *protocol, struct sockaddr *address)
{
TRACE(("tcp_bind(%p) on address %s\n", protocol,
AddressString(sDomain, address, true).Data()));
TCPConnection *connection = (TCPConnection *)protocol;
return connection->Bind(address);
DUMP_HASH;
return ((TCPConnection *)protocol)->Bind(address);
}
status_t
tcp_unbind(net_protocol *protocol, struct sockaddr *address)
{
DUMP_HASH;
return ((TCPConnection *)protocol)->Unbind(address);
}
@@ -199,6 +235,17 @@ 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 and \a ack. The packet is then sent on \a route
*/
status_t
tcp_send_segment(net_buffer *buffer, net_route *route, uint16 flags, uint32 seq, uint32 ack)
{
return B_ERROR;
}
status_t
tcp_receive_data(net_buffer *buffer)
{
@@ -229,10 +276,8 @@ tcp_init()
{
status_t status;
#if 0
sDomain = sStackModule->get_domain(AF_INET);
sAddressModule = sDomain->address_module;
#endif
sDomain = NULL;
sAddressModule = NULL;
status = get_module(NET_STACK_MODULE_NAME, (module_info **)&sStackModule);
if (status < B_OK)