* Big rework of TCP - doesn't have any additional features yet, though, even

though it can now connect to itself (and should, in theory, also be able
  to connect to other TCP implemenations).
* Separated the big state machine from ReceiveData() into several smaller sized
  methods.
* Moved reset and acknowledgement handling for all kind of receives to a common
  place in tcp_receive_data().
* Removed locking for now - the previous locking approach was wrong due to a
  number of reasons: the previous version deadlocked when it had to connect
  locally, and it also didn't take into account that TCP is a full-duplex
  protocol; it's not wise to only be able to manage one direction at a time
  if that's not really needed.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19329 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-11-19 23:33:24 +00:00
parent fae6fb6d69
commit 30ea523ad1
4 changed files with 609 additions and 416 deletions

View File

@ -86,38 +86,59 @@ tcp_segment::~tcp_segment()
TCPConnection::TCPConnection(net_socket *socket)
:
fLastByteAckd(0), //system_time()),
fNextByteToSend(fLastByteAckd),
fNextByteToWrite(fLastByteAckd + 1),
fNextByteToRead(0),
fNextByteExpected(0),
fLastByteReceived(0),
fAvgRTT(TCP_INITIAL_RTT),
fLastAcknowledged(0), //system_time()),
fSendNext(fLastAcknowledged),
fSendWindow(0),
fSendBuffer(NULL),
fRoute(NULL),
fReceiveNext(0),
fReceiveWindow(0),
fAvgRTT(TCP_INITIAL_RTT),
fReceiveBuffer(NULL),
fState(CLOSED),
fError(B_OK),
fRoute(NULL)
fError(B_OK)
{
benaphore_init(&fLock, "TCPConnection");
gStackModule->init_timer(&fTimer, _TimeWait, this);
list_init(&fReorderQueue);
list_init(&fWaitQueue);
benaphore_init(&fReceiveLock, "tcp receive");
benaphore_init(&fSendLock, "tcp send");
fAcceptSemaphore = create_sem(0, "tcp accept");
}
TCPConnection::~TCPConnection()
{
benaphore_destroy(&fLock);
gStackModule->set_timer(&fTimer, -1);
benaphore_destroy(&fReceiveLock);
benaphore_destroy(&fSendLock);
delete_sem(fAcceptSemaphore);
}
status_t
TCPConnection::InitCheck() const
{
if (fReceiveLock.sem < B_OK)
return fReceiveLock.sem;
if (fSendLock.sem < B_OK)
return fSendLock.sem;
if (fAcceptSemaphore < B_OK)
return fAcceptSemaphore;
return B_OK;
}
inline bool
TCPConnection::_IsAcknowledgeValid(uint32 acknowledge)
{
return fLastByteAckd > fNextByteToSend
? acknowledge >= fLastByteAckd || acknowledge <= fNextByteToSend
: acknowledge >= fLastByteAckd && acknowledge <= fNextByteToSend;
dprintf("last byte ackd %lu, next byte to send %lu, ack %lu\n", fLastAcknowledged, fSendNext, acknowledge);
return fLastAcknowledged > fSendNext
? acknowledge >= fLastAcknowledged || acknowledge <= fSendNext
: acknowledge >= fLastAcknowledged && acknowledge <= fSendNext;
}
@ -126,16 +147,16 @@ TCPConnection::_IsSequenceValid(uint32 sequence, uint32 length)
{
uint32 end = sequence + length;
if (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF) {
return sequence >= fNextByteToRead
&& end <= fNextByteToRead + TCP_MAX_RECV_BUF;
if (fReceiveNext < fReceiveNext + TCP_MAX_RECV_BUF) {
return sequence >= fReceiveNext
&& end <= fReceiveNext + TCP_MAX_RECV_BUF;
}
// integer overflow
return (sequence >= fNextByteToRead
|| sequence <= fNextByteToRead + TCP_MAX_RECV_BUF)
&& (end >= fNextByteToRead
|| end <= fNextByteToRead + TCP_MAX_RECV_BUF);
return (sequence >= fReceiveNext
|| sequence <= fReceiveNext + TCP_MAX_RECV_BUF)
&& (end >= fReceiveNext
|| end <= fReceiveNext + TCP_MAX_RECV_BUF);
}
@ -143,15 +164,7 @@ 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);
// nothing to do here...
return B_OK;
}
@ -159,23 +172,28 @@ TCPConnection::Open()
status_t
TCPConnection::Close()
{
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
TRACE(("TCP:%p.Close()\n", this));
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
fState = CLOSED;
return B_OK;
}
tcp_state nextState = CLOSED;
tcp_state previousState = fState;
if (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED)
nextState = FINISH_SENT;
if (fState == FINISH_RECEIVED)
nextState = WAIT_FOR_FINISH_ACKNOWLEDGE;
fState = FINISH_SENT;
else if (fState == FINISH_RECEIVED)
fState = WAIT_FOR_FINISH_ACKNOWLEDGE;
else
fState = CLOSED;
status_t status = _SendQueuedData(TCP_FLAG_FINISH | TCP_FLAG_ACKNOWLEDGE, false);
if (status != B_OK)
if (status != B_OK) {
fState = previousState;
return status;
}
fState = nextState;
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
// TODO: do i need to wait until fState returns to CLOSED?
return B_OK;
@ -187,14 +205,8 @@ 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);
}
// TODO: if this connection is not in the hash, we don't have to call this one
remove_connection(this);
return B_OK;
}
@ -212,16 +224,16 @@ TCPConnection::Connect(const struct sockaddr *address)
if (address->sa_family != AF_INET)
return EAFNOSUPPORT;
benaphore_lock(&gConnectionLock);
// want to release lock later, so no autolock
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
// Can only call Connect from CLOSED or LISTEN states
// Can only call connect() from CLOSED or LISTEN states
// otherwise connection is considered already connected
if (fState != CLOSED && fState != LISTEN) {
benaphore_unlock(&gConnectionLock);
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)
return EISCONN;
}
TRACE((" TCP: Connect(): in state %d\n", fState));
// get a net_route if there isn't one
@ -229,51 +241,36 @@ TCPConnection::Connect(const struct sockaddr *address)
if (fRoute == NULL) {
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
TRACE((" TCP: Connect(): Using Route %p\n", fRoute));
if (fRoute == NULL) {
benaphore_unlock(&gConnectionLock);
if (fRoute == NULL)
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));
}
gAddressModule->set_to((sockaddr *)&socket->peer, address);
// 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);
status_t status = insert_connection(this);
if (status < B_OK) {
TRACE((" TCP: Connect(): could not add connection: %s!\n", strerror(status)));
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_FLAG_SYNCHRONIZE, false);
if (status != B_OK)
return status;
fState = SYNCHRONIZE_SENT;
// send SYN
status = _SendQueuedData(TCP_FLAG_SYNCHRONIZE, false);
if (status != B_OK) {
fState = CLOSED;
return status;
}
// TODO: wait until 3-way handshake is complete
TRACE((" TCP: Connect(): Connection complete\n"));
return B_OK;
@ -284,7 +281,19 @@ status_t
TCPConnection::Accept(struct net_socket **_acceptedSocket)
{
TRACE(("TCP:%p.Accept()\n", this));
return B_ERROR;
// TODO: test for pending sockets
// TODO: test for non-blocking I/O
status_t status;
do {
status = acquire_sem(fAcceptSemaphore);
if (status < B_OK)
return status;
status = gSocketModule->dequeue_connected(socket, _acceptedSocket);
} while (status < B_OK);
return status;
}
@ -297,8 +306,11 @@ TCPConnection::Bind(sockaddr *address)
if (address->sa_family != AF_INET)
return EAFNOSUPPORT;
BenaphoreLocker hashLock(&gConnectionLock);
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
// TODO: there is no lock yet for these things...
if (fState != CLOSED)
return EISCONN;
// let IP check whether there is an interface that supports the given address:
status_t status = next->module->bind(next, address);
@ -306,21 +318,18 @@ TCPConnection::Bind(sockaddr *address)
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) {
if (gAddressModule->get_port((sockaddr *)&socket->address) == 0) {
// assign ephemeral port
// TODO: use port 40000 and following for now
static int e = 40000;
gAddressModule->set_port((sockaddr *)&socket->address, htons(e++));
status = insert_connection(this);
} 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;
status = insert_connection(this);
}
return B_OK;
return status;
}
@ -329,10 +338,10 @@ TCPConnection::Unbind(struct sockaddr *address)
{
TRACE(("TCP:%p.Unbind()\n", this ));
BenaphoreLocker hashLock(&gConnectionLock);
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
// TODO: there is no lock yet for these things...
status_t status = hash_remove(gConnectionHash, (void *)this);
status_t status = remove_connection(this);
if (status != B_OK)
return status;
@ -346,9 +355,9 @@ status_t
TCPConnection::Listen(int count)
{
TRACE(("TCP:%p.Listen()\n", this));
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
if (fState != CLOSED)
return B_ERROR;
return B_BAD_VALUE;
fState = LISTEN;
return B_OK;
@ -359,6 +368,7 @@ status_t
TCPConnection::Shutdown(int direction)
{
TRACE(("TCP:%p.Shutdown()\n", this));
// TODO: implement shutdown!
return B_ERROR;
}
@ -370,8 +380,8 @@ status_t
TCPConnection::SendData(net_buffer *buffer)
{
TRACE(("TCP:%p.SendData()\n", this));
size_t bufferSize = buffer->size;
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fSendLock);
if (fSendBuffer != NULL) {
status_t status = gBufferModule->merge(fSendBuffer, buffer, true);
if (status != B_OK)
@ -379,28 +389,15 @@ TCPConnection::SendData(net_buffer *buffer)
} else
fSendBuffer = buffer;
fNextByteToWrite += bufferSize;
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, 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);
BenaphoreLocker lock(&fSendLock);
if (fSendBuffer != NULL)
return TCP_MAX_SEND_BUF - fSendBuffer->size;
@ -413,7 +410,7 @@ TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
{
TRACE(("TCP:%p.ReadData()\n", this));
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fReceiveLock);
// must be in a synchronous state
if (fState != ESTABLISHED || fState != FINISH_SENT || fState != FINISH_ACKNOWLEDGED) {
@ -441,7 +438,7 @@ size_t
TCPConnection::ReadAvailable()
{
TRACE(("TCP:%p.ReadAvailable()\n", this));
BenaphoreLocker lock(&fLock);
BenaphoreLocker lock(&fReceiveLock);
if (fReceiveBuffer != NULL)
return fReceiveBuffer->size;
@ -450,7 +447,7 @@ TCPConnection::ReadAvailable()
/*!
You must hold the connection's lock when calling this method
You must hold the connection's receive lock when calling this method
*/
status_t
TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
@ -458,7 +455,7 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
TRACE(("TCP:%p.EnqueueReceivedData(%p, %lu)\n", this, buffer, sequence));
status_t status;
if (sequence == fNextByteExpected) {
if (sequence == fReceiveNext) {
// first check if the received buffer meets up with the first
// segment in the ReorderQueue
tcp_segment *next;
@ -478,16 +475,16 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
break;
}
fNextByteExpected += buffer->size;
if (fReceiveBuffer == NULL)
fReceiveBuffer = buffer;
else {
fReceiveNext += buffer->size;
if (fReceiveBuffer != NULL) {
status = gBufferModule->merge(fReceiveBuffer, buffer, true);
if (status < B_OK) {
fNextByteExpected -= buffer->size;
fReceiveNext -= buffer->size;
return status;
}
}
} else
fReceiveBuffer = buffer;
} else {
// add this buffer into the ReorderQueue in the correct place
// creating a new tcp_segment if necessary
@ -527,195 +524,198 @@ TCPConnection::_EnqueueReceivedData(net_buffer *buffer, uint32 sequence)
status_t
TCPConnection::ReceiveData(net_buffer *buffer)
TCPConnection::DelayedAcknowledge()
{
BenaphoreLocker lock(&fLock);
TRACE(("TCP:%p.ReceiveData()\n", this));
// TODO: use timer instead and/or piggyback on send
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
}
NetBufferHeader<tcp_header> bufferHeader(buffer);
if (bufferHeader.Status() < B_OK)
return bufferHeader.Status();
tcp_header &header = bufferHeader.Data();
status_t
TCPConnection::SendAcknowledge()
{
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
}
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 dataLength = 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_FLAG_ACKNOWLEDGE)
return _Reset(byteAckd, 0);
status_t
TCPConnection::UpdateTimeWait()
{
return B_OK;
}
return _Reset(0, byteRcvd + dataLength);
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;
int32
TCPConnection::ListenReceive(tcp_segment_header& segment, net_buffer *buffer)
{
// Essentially, we accept only TCP_FLAG_SYNCHRONIZE in this state,
// but the error behaviour differs
if (segment.flags & TCP_FLAG_RESET)
return DROP;
if (segment.flags & TCP_FLAG_ACKNOWLEDGE)
return DROP | RESET;
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
return DROP;
case SYNCHRONIZE_SENT:
if (header.flags & TCP_FLAG_RESET) {
// TODO: drop broadcast/multicast
// spawn new connection for accept()
net_socket *newSocket;
if (gSocketModule->spawn_pending_socket(socket, &newSocket) < B_OK)
return DROP;
gAddressModule->set_to((sockaddr *)&newSocket->address,
(sockaddr *)&buffer->destination);
gAddressModule->set_to((sockaddr *)&newSocket->peer,
(sockaddr *)&buffer->source);
TCPConnection *connection = (TCPConnection *)newSocket->first_protocol;
// TODO: proper error handling!
connection->fRoute = gDatalinkModule->get_route(gDomain,
(sockaddr *)&newSocket->address);
if (connection->fRoute == NULL)
return DROP;
if (insert_connection(connection) < B_OK)
return DROP;
connection->fState = SYNCHRONIZE_SENT;
benaphore_lock(&connection->fSendLock);
status_t status = connection->_SendQueuedData(
TCP_FLAG_SYNCHRONIZE | TCP_FLAG_ACKNOWLEDGE, false);
benaphore_unlock(&connection->fSendLock);
if (status < B_OK)
return DROP;
return connection->Receive(segment, buffer);
}
int32
TCPConnection::SynchronizeSentReceive(tcp_segment_header& segment, net_buffer *buffer)
{
if (segment.flags & TCP_FLAG_RESET) {
fError = ECONNREFUSED;
fState = CLOSED;
return B_ERROR;
return DROP;
}
if (header.flags & TCP_FLAG_ACKNOWLEDGE && !_IsAcknowledgeValid(byteAckd))
return _Reset(byteAckd, 0);
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
&& !_IsAcknowledgeValid(segment.acknowledge))
return DROP | RESET;
if (header.flags & TCP_FLAG_SYNCHRONIZE) {
fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1;
flags |= TCP_FLAG_ACKNOWLEDGE;
fLastByteAckd = byteAckd;
if ((segment.flags & TCP_FLAG_SYNCHRONIZE) == 0)
return DROP;
// cancel resend of this segment
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
nextState = ESTABLISHED;
else {
nextState = SYNCHRONIZE_RECEIVED;
flags |= TCP_FLAG_SYNCHRONIZE;
}
}
break;
fReceiveNext = segment.sequence + 1;
fLastAcknowledged = segment.acknowledge;
case SYNCHRONIZE_RECEIVED:
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd))
if (segment.flags & TCP_FLAG_ACKNOWLEDGE) {
// the connection has been established
fState = ESTABLISHED;
else
_Reset(byteAckd, 0);
break;
} else {
// simultaneous open
fState = SYNCHRONIZE_RECEIVED;
}
return Receive(segment, buffer) | IMMEDIATE_ACKNOWLEDGE;
}
int32
TCPConnection::Receive(tcp_segment_header& segment, net_buffer *buffer)
{
TRACE(("TCP:%p.ReceiveData(): Connection in state %d received packet %p with flags 0x%x, seq %lu, ack %lu!\n",
this, fState, buffer, segment.flags, segment.sequence, segment.acknowledge));
// TODO: rethink locking!
default:
// In a synchronized state.
// first check that the received sequence number is good
if (_IsSequenceValid(byteRcvd, dataLength)) {
if (_IsSequenceValid(segment.sequence, buffer->size)) {
// If a valid RST was received, terminate the connection.
if (header.flags & TCP_FLAG_RESET) {
if (segment.flags & TCP_FLAG_RESET) {
fError = ECONNREFUSED;
fState = CLOSED;
return B_ERROR;
return DROP;
}
}
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd)) {
fLastByteAckd = byteAckd;
if (fLastByteAckd == fNextByteToWrite) {
int32 action = KEEP;
status_t status;
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) != 0
&& _IsAcknowledgeValid(segment.acknowledge)) {
fLastAcknowledged = segment.acknowledge;
if (fLastAcknowledged == fSendNext) {
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
nextState = CLOSED;
status = hash_remove(gConnectionHash, this);
fState = CLOSED;
status = remove_connection(this);
if (status != B_OK)
return status;
}
if (fState == CLOSING) {
nextState = TIME_WAIT;
status = hash_remove(gConnectionHash, this);
return DROP;
} else if (fState == CLOSING) {
fState = TIME_WAIT;
status = remove_connection(this);
if (status != B_OK)
return status;
return DROP;
} else if (fState == FINISH_SENT)
fState = FINISH_ACKNOWLEDGED;
}
if (fState == FINISH_SENT) {
nextState = FINISH_ACKNOWLEDGED;
}
}
}
if (header.flags & TCP_FLAG_FINISH) {
if (segment.flags & TCP_FLAG_FINISH) {
fReceiveNext++;
// other side is closing connection; change states
switch (fState) {
case ESTABLISHED:
nextState = FINISH_RECEIVED;
fNextByteExpected++;
fState = FINISH_RECEIVED;
break;
case FINISH_ACKNOWLEDGED:
nextState = TIME_WAIT;
fNextByteExpected++;
fState = TIME_WAIT;
break;
case FINISH_RECEIVED:
if (fLastByteAckd == fNextByteToWrite) {
// our FIN has been ACKd: go to TIME_WAIT
nextState = TIME_WAIT;
status = hash_remove(gConnectionHash, this);
if (fLastAcknowledged == fSendNext) {
fState = TIME_WAIT;
status = remove_connection(this);
if (status != B_OK)
return status;
gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
return DROP;
//gStackModule->set_timer(&fTimer, TCP_MAX_SEGMENT_LIFETIME);
} else
nextState = CLOSING;
fNextByteExpected++;
fState = CLOSING;
break;
default:
break;
}
}
flags |= TCP_FLAG_ACKNOWLEDGE;
if (fState != FINISH_ACKNOWLEDGED)
action |= ACKNOWLEDGE;
} else {
// out-of-order packet received. remind the other side of where we are
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, true);
// out-of-order packet received, remind the other side of where we are
return DROP | ACKNOWLEDGE;
}
break;
}
TRACE(("TCP %p.ReceiveData():Entering state %d\n", this, fState));
TRACE(("TCP %p.Receive():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);
status = _EnqueueReceivedData(buffer, segment.sequence);
if (status != B_OK)
return status;
return DROP;
} else
gBufferModule->free(buffer);
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE) {
status = _SendQueuedData(flags, false);
if (status != B_OK)
return status;
}
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE)
action &= ACKNOWLEDGE;
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_FLAG_RESET | (acknowledge == 0 ? 0 : TCP_FLAG_ACKNOWLEDGE),
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;
return action;
}
@ -736,9 +736,10 @@ TCPConnection::ResendSegment(struct net_timer *timer, void *data)
/*!
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.
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.
The send lock must be held before calling.
*/
status_t
TCPConnection::_SendQueuedData(uint16 flags, bool empty)
@ -750,49 +751,51 @@ TCPConnection::_SendQueuedData(uint16 flags, bool empty)
net_buffer *buffer;
uint32 effectiveWindow = min_c(next->module->get_mtu(next,
(sockaddr *)&socket->address), fNextByteToWrite - fNextByteToSend);
(sockaddr *)&socket->address), fSendWindow);
if (empty || effectiveWindow == 0 || fSendBuffer == NULL || fSendBuffer->size == 0) {
if (flags == 0) {
// there is just nothing left to do
return B_OK;
}
buffer = gBufferModule->create(256);
TRACE(("TCP: Sending Buffer %p\n", buffer));
if (buffer == NULL)
return ENOBUFS;
} else {
if (effectiveWindow == fSendBuffer->size) {
buffer = fSendBuffer;
if (effectiveWindow == fSendBuffer->size)
fSendBuffer = NULL;
else
fSendBuffer = gBufferModule->split(fSendBuffer, effectiveWindow);
} else
buffer = 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,
TRACE(("TCP:%p.SendQueuedData() buffer %p, from address %s to %s\n", this,
buffer,
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data(),
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);
status_t status = add_tcp_header(buffer, flags, fSendNext,
fReceiveNext, fReceiveWindow);
if (status != B_OK) {
gBufferModule->free(buffer);
return status;
}
// Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0 && (fState == CLOSED || fState == LISTEN))
fNextByteToSend++;
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0)
fSendNext++;
// Only count 1 FIN, the 1 sent when transitioning from
// ESTABLISHED, SYNCHRONIZE_RECEIVED or FINISH_RECEIVED
if ((flags & TCP_FLAG_FINISH) != 0
&& (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED || fState == FINISH_RECEIVED))
fNextByteToSend++;
if ((flags & TCP_FLAG_FINISH) != 0)
fSendNext++;
fNextByteToSend += size;
fSendNext += size;
#if 0
tcp_segment *segment = new(std::nothrow)
@ -830,11 +833,10 @@ 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;
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;
return gAddressModule->hash_address_pair(key->local, key->peer) % range;
}

View File

@ -23,6 +23,8 @@ class TCPConnection : public net_protocol {
TCPConnection(net_socket *socket);
~TCPConnection();
status_t InitCheck() const;
status_t Open();
status_t Close();
status_t Free();
@ -33,17 +35,24 @@ class TCPConnection : public net_protocol {
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);
tcp_state State() const { return fState; }
status_t DelayedAcknowledge();
status_t SendAcknowledge();
status_t UpdateTimeWait();
int32 ListenReceive(tcp_segment_header& segment, net_buffer *buffer);
int32 SynchronizeSentReceive(tcp_segment_header& segment,
net_buffer *buffer);
int32 Receive(tcp_segment_header& segment, 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); }
static int32 HashOffset() { return offsetof(TCPConnection, fHashNext); }
private:
bool _IsAcknowledgeValid(uint32 acknowledge);
@ -51,34 +60,38 @@ class TCPConnection : public net_protocol {
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);
uint32 fLastByteAckd;
uint32 fNextByteToSend;
uint32 fNextByteToWrite;
TCPConnection *fHashNext;
uint32 fNextByteToRead;
uint32 fNextByteExpected;
uint32 fLastByteReceived;
bigtime_t fAvgRTT;
benaphore fSendLock;
benaphore fReceiveLock;
benaphore fLock;
sem_id fAcceptSemaphore;
uint32 fLastAcknowledged;
uint32 fSendNext;
uint32 fSendWindow;
net_buffer *fSendBuffer;
net_route *fRoute;
// TODO: don't use a net_route, but a net_route_info!!!
uint32 fReceiveNext;
uint32 fReceiveWindow;
bigtime_t fAvgRTT;
net_buffer *fReceiveBuffer;
tcp_state fState;
status_t fError;
vint32 fDelayedAcknowledge;
struct list fReorderQueue;
struct list fWaitQueue;
TCPConnection *fHashLink;
tcp_state fState;
status_t fError;
benaphore fLock;
// timer
net_timer fTimer;
net_route *fRoute;
// TODO: don't use a net_route, but a net_route_info!!!
};
#endif // TCP_CONNECTION_H

View File

@ -44,9 +44,10 @@ net_domain *gDomain;
net_address_module_info *gAddressModule;
net_buffer_module_info *gBufferModule;
net_datalink_module_info *gDatalinkModule;
net_socket_module_info *gSocketModule;
net_stack_module_info *gStackModule;
hash_table *gConnectionHash;
benaphore gConnectionLock;
recursive_lock gConnectionLock;
#ifdef TRACE_TCP
@ -55,7 +56,7 @@ benaphore gConnectionLock;
void
tcp_dump_hash()
{
BenaphoreLocker lock(&gConnectionLock);
RecursiveLocker lock(&gConnectionLock);
if (gDomain == NULL) {
TRACE(("Unable to dump TCP Connections!\n"));
return;
@ -66,7 +67,7 @@ tcp_dump_hash()
hash_rewind(gConnectionHash, &iterator);
TRACE(("Active TCP Connections:\n"));
while ((connection = (TCPConnection *)hash_next(gConnectionHash, &iterator)) != NULL) {
TRACE((" TCPConnection %p: %s, %s\n", connection,
TRACE((" TCPConnection %p: local %s, peer %s\n", connection,
AddressString(gDomain, (sockaddr *)&connection->socket->address, true).Data(),
AddressString(gDomain, (sockaddr *)&connection->socket->peer, true).Data()));
}
@ -104,8 +105,8 @@ set_domain(net_interface *interface = NULL)
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)
add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
uint32 acknowledge, uint16 advertisedWindow)
{
buffer->protocol = IPPROTO_TCP;
@ -117,16 +118,19 @@ add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
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.sequence = htonl(sequence);
header.acknowledge = (flags & TCP_FLAG_ACKNOWLEDGE) ? htonl(acknowledge) : 0;
header.reserved = 0;
header.header_length = 5;
header.header_length = sizeof(tcp_header) >> 2;
// currently no options supported
header.flags = (uint8)flags;
header.advertised_window = htons(advertisedWindow);
header.checksum = 0;
header.urgent_ptr = 0;
// urgent pointer not supported
header.urgent_offset = 0;
// TODO: urgent pointer not yet supported
TRACE(("add_tcp_header(): buffer %p, flags 0x%x, seq %lu, ack %lu\n", buffer,
flags, sequence, acknowledge));
// compute and store checksum
Checksum checksum;
@ -137,20 +141,134 @@ add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence, uint32 ack,
<< (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;
}
status_t
reply_with_reset(tcp_segment_header& segment, net_buffer *buffer)
{
TRACE(("TCP: Sending RST...\n"));
net_buffer *reply = gBufferModule->create(512);
if (reply == NULL)
return B_NO_MEMORY;
gAddressModule->set_to((sockaddr *)&reply->source,
(sockaddr *)&buffer->destination);
gAddressModule->set_to((sockaddr *)&reply->destination,
(sockaddr *)&buffer->source);
uint8 flags = TCP_FLAG_RESET;
uint32 acknowledge = 0;
uint32 sequence = 0;
if ((segment.flags & TCP_FLAG_ACKNOWLEDGE) == 0) {
flags |= TCP_FLAG_ACKNOWLEDGE;
acknowledge = segment.sequence + buffer->size;
} else
sequence = segment.acknowledge;
status_t status = add_tcp_header(reply, flags,
sequence, acknowledge, 0);
if (status == B_OK) {
status = gDomain->module->send_data(NULL, reply);
}
if (status != B_OK)
gBufferModule->free(reply);
return status;
}
TCPConnection *
lookup_connection(sockaddr *local, sockaddr *peer)
{
tcp_connection_key key;
key.local = local;
key.peer = peer;
return (TCPConnection *)hash_lookup(gConnectionHash, &key);
}
status_t
remove_connection(TCPConnection *connection)
{
RecursiveLocker hashLock(&gConnectionLock);
return hash_remove(gConnectionHash, connection);
}
status_t
insert_connection(TCPConnection *connection)
{
RecursiveLocker hashLock(&gConnectionLock);
tcp_connection_key key;
key.local = (sockaddr *)&connection->socket->address;
key.peer = (sockaddr *)&connection->socket->peer;
if (hash_lookup(gConnectionHash, &key) != NULL)
return EADDRINUSE;
return hash_insert(gConnectionHash, connection);
}
TCPConnection *
find_connection(sockaddr *local, sockaddr *peer)
{
TCPConnection *connection = lookup_connection(local, peer);
if (connection != NULL) {
TRACE(("TCP: Received packet corresponds to explicit connection %p\n", connection));
return connection;
}
// no explicit connection exists, check for wildcard connections
sockaddr wildcard;
gAddressModule->set_to_empty_address(&wildcard);
connection = lookup_connection(local, &wildcard);
if (connection != NULL) {
TRACE(("TCP: Received packet corresponds to wildcard connection %p\n", connection));
return connection;
}
sockaddr localWildcard;
gAddressModule->set_to_empty_address(&localWildcard);
gAddressModule->set_port(&localWildcard, gAddressModule->get_port(local));
connection = lookup_connection(&localWildcard, &wildcard);
if (connection != NULL) {
TRACE(("TCP: Received packet corresponds to local wildcard connection %p\n", connection));
return connection;
}
// no matching connection exists
return NULL;
}
// #pragma mark - protocol API
net_protocol *
tcp_init_protocol(net_socket *socket)
{
DUMP_TCP_HASH;
socket->protocol = IPPROTO_TCP;
TCPConnection *protocol = new (std::nothrow) TCPConnection(socket);
if (protocol == NULL)
return NULL;
if (protocol->InitCheck() != B_OK) {
delete protocol;
return NULL;
}
TRACE(("Creating new TCPConnection: %p\n", protocol));
return protocol;
}
@ -259,7 +377,8 @@ status_t
tcp_send_routed_data(net_protocol *protocol, struct net_route *route,
net_buffer *buffer)
{
return ((TCPConnection *)protocol)->SendRoutedData(route, buffer);
// TCP never sends routed data
return B_ERROR;
}
@ -313,67 +432,89 @@ tcp_receive_data(net_buffer *buffer)
tcp_header &header = bufferHeader.Data();
tcp_connection_key key;
key.peer = (struct sockaddr *)&buffer->source;
key.local = (struct sockaddr *)&buffer->destination;
uint16 headerLength = header.HeaderLength();
if (headerLength < sizeof(tcp_header))
return B_BAD_DATA;
// TODO: check TCP Checksum
// compute checksum using a pseudo IP header
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);
if (checksum != 0)
return B_BAD_DATA;
gAddressModule->set_port((struct sockaddr *)&buffer->source, header.source_port);
gAddressModule->set_port((struct sockaddr *)&buffer->destination, header.destination_port);
TRACE((" Looking for: peer %s, local %s\n",
AddressString(gDomain, (sockaddr *)&buffer->source, true).Data(),
AddressString(gDomain, (sockaddr *)&buffer->destination, true).Data()));
DUMP_TCP_HASH;
BenaphoreLocker hashLock(&gConnectionLock);
TCPConnection *connection = (TCPConnection *)hash_lookup(gConnectionHash, &key);
TRACE(("TCP: Received packet corresponds to connection %p\n", connection));
// TODO: process options!
tcp_segment_header segment;
segment.sequence = header.Sequence();
segment.acknowledge = header.Acknowledge();
segment.advertised_window = header.AdvertisedWindow();
segment.urgent_offset = header.UrgentOffset();
segment.flags = header.flags;
bufferHeader.Remove(headerLength);
// we no longer need to keep the header around
RecursiveLocker hashLock(&gConnectionLock);
int32 segmentAction = DROP;
TCPConnection *connection = find_connection((struct sockaddr *)&buffer->destination,
(struct sockaddr *)&buffer->source);
if (connection != NULL) {
return connection->ReceiveData(buffer);
} else {
/* TODO:
No explicit connection exists. Check for wildcard connections:
First check if any connections exist where local = IPADDR_ANY
then check when local = peer = IPADDR_ANY.
port numbers always remain the same */
switch (connection->State()) {
case CLOSED:
case TIME_WAIT:
connection->UpdateTimeWait();
break;
// If no connection exists (and RESET is not set) send RESET
if ((header.flags & TCP_FLAG_RESET) == 0) {
case LISTEN:
segmentAction = connection->ListenReceive(segment, buffer);
break;
case SYNCHRONIZE_SENT:
segmentAction = connection->SynchronizeSentReceive(segment, buffer);
break;
case SYNCHRONIZE_RECEIVED:
case ESTABLISHED:
case FINISH_RECEIVED:
case WAIT_FOR_FINISH_ACKNOWLEDGE:
case FINISH_SENT:
case FINISH_ACKNOWLEDGED:
case CLOSING:
segmentAction = connection->Receive(segment, buffer);
break;
}
// process acknowledge action as asked for by the *Receive() method
if (segmentAction & IMMEDIATE_ACKNOWLEDGE)
connection->SendAcknowledge();
else if (segmentAction & ACKNOWLEDGE)
connection->DelayedAcknowledge();
} else if ((segment.flags & TCP_FLAG_RESET) == 0)
segmentAction = DROP | RESET;
if (segmentAction & RESET) {
// send reset
TRACE(("TCP: Connection does not exist!\n"));
net_buffer *reply = gBufferModule->create(512);
if (reply == NULL)
return B_NO_MEMORY;
gAddressModule->set_to((sockaddr *)&reply->source,
(sockaddr *)&buffer->destination);
gAddressModule->set_to((sockaddr *)&reply->destination,
(sockaddr *)&buffer->source);
reply_with_reset(segment, buffer);
}
if (segmentAction & DROP)
gBufferModule->free(buffer);
uint32 sequence, acknowledge;
uint16 flags;
if (header.flags & TCP_FLAG_ACKNOWLEDGE) {
sequence = ntohl(header.acknowledge_num);
acknowledge = 0;
flags = TCP_FLAG_RESET;
} else {
sequence = 0;
acknowledge = ntohl(header.sequence_num) + 1
+ buffer->size - ((uint32)header.header_length << 2);
flags = TCP_FLAG_RESET | TCP_FLAG_ACKNOWLEDGE;
}
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);
}
if (status != B_OK) {
gBufferModule->free(reply);
return status;
}
}
}
return B_OK;
}
@ -410,46 +551,51 @@ tcp_init()
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 **)&gDatalinkModule);
status = get_module(NET_SOCKET_MODULE_NAME, (module_info **)&gSocketModule);
if (status < B_OK)
goto err2;
status = get_module(NET_DATALINK_MODULE_NAME, (module_info **)&gDatalinkModule);
if (status < B_OK)
goto err3;
gConnectionHash = hash_init(MAX_HASH_TCP, TCPConnection::HashOffset(),
&TCPConnection::Compare, &TCPConnection::Hash);
if (gConnectionHash == NULL)
goto err3;
status = benaphore_init(&gConnectionLock, "TCP Hash Lock");
if (status < B_OK)
goto err4;
status = recursive_lock_init(&gConnectionLock, "tcp connection hash");
if (status < B_OK)
goto err5;
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;
goto err6;
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;
goto err6;
status = gStackModule->register_domain_receiving_protocol(AF_INET, IPPROTO_TCP,
"network/protocols/tcp/v1");
if (status < B_OK)
goto err5;
goto err6;
return B_OK;
err6:
recursive_lock_destroy(&gConnectionLock);
err5:
benaphore_destroy(&gConnectionLock);
err4:
hash_uninit(gConnectionHash);
err3:
err4:
put_module(NET_DATALINK_MODULE_NAME);
err3:
put_module(NET_SOCKET_MODULE_NAME);
err2:
put_module(NET_BUFFER_MODULE_NAME);
err1:
@ -463,9 +609,10 @@ err1:
static status_t
tcp_uninit()
{
benaphore_destroy(&gConnectionLock);
recursive_lock_destroy(&gConnectionLock);
hash_uninit(gConnectionHash);
put_module(NET_DATALINK_MODULE_NAME);
put_module(NET_SOCKET_MODULE_NAME);
put_module(NET_BUFFER_MODULE_NAME);
put_module(NET_STACK_MODULE_NAME);

View File

@ -11,13 +11,18 @@
#include <net_buffer.h>
#include <net_datalink.h>
#include <net_socket.h>
#include <net_stack.h>
#include <util/khash.h>
#include <ByteOrder.h>
#include <sys/socket.h>
class TCPConnection;
typedef enum {
// establishing a connection
CLOSED,
@ -40,8 +45,8 @@ typedef enum {
struct tcp_header {
uint16 source_port;
uint16 destination_port;
uint32 sequence_num;
uint32 acknowledge_num;
uint32 sequence;
uint32 acknowledge;
struct {
#if B_HOST_IS_LENDIAN == 1
uint8 reserved : 4;
@ -54,7 +59,14 @@ struct tcp_header {
uint8 flags;
uint16 advertised_window;
uint16 checksum;
uint16 urgent_ptr;
uint16 urgent_offset;
uint32 HeaderLength() const { return (uint32)header_length << 2; }
uint32 Sequence() const { return ntohl(sequence); }
uint32 Acknowledge() const { return ntohl(acknowledge); }
uint16 AdvertisedWindow() const { return ntohs(advertised_window); }
uint16 Checksum() const { return ntohs(checksum); }
uint16 UrgentOffset() const { return ntohs(urgent_offset); }
};
// TCP flag constants
@ -67,6 +79,22 @@ struct tcp_header {
#define TCP_FLAG_ECN 0x40 // Explicit Congestion Notification echo
#define TCP_FLAG_CWR 0x80 // Congestion Window Reduced
struct tcp_segment_header {
uint32 sequence;
uint32 acknowledge;
uint16 advertised_window;
uint16 urgent_offset;
uint8 flags;
};
enum tcp_segment_action {
KEEP = 0x00,
DROP = 0x01,
RESET = 0x02,
ACKNOWLEDGE = 0x04,
IMMEDIATE_ACKNOWLEDGE = 0x08,
};
struct tcp_connection_key {
const sockaddr *local;
const sockaddr *peer;
@ -77,12 +105,15 @@ extern net_domain *gDomain;
extern net_address_module_info *gAddressModule;
extern net_buffer_module_info *gBufferModule;
extern net_datalink_module_info *gDatalinkModule;
extern net_socket_module_info *gSocketModule;
extern net_stack_module_info *gStackModule;
extern hash_table *gConnectionHash;
extern benaphore gConnectionLock;
//extern hash_table *gConnectionHash;
//extern benaphore gConnectionLock;
status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
uint32 ack, uint16 advertisedWindow);
status_t remove_connection(TCPConnection *connection);
status_t insert_connection(TCPConnection *connection);
#endif // TCP_H