* Some cleanup, renamed some flags/constants to be a bit clearer (even though the states

now deviate from what other use to have, ie. FreeBSD).
* Replaced macros with inline methods.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19215 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-11-06 14:09:37 +00:00
parent 088a32925d
commit 31d3ef17d8
4 changed files with 112 additions and 75 deletions

View File

@ -47,15 +47,6 @@
#define TCP_MAX_SEND_BUF 1024
#define TCP_MAX_RECV_BUF TCP_MAX_SEND_BUF
#define TCP_IS_GOOD_ACK(x) (fLastByteAckd > fNextByteToSend ? \
((x) >= fLastByteAckd || (x) <= fNextByteToSend) : \
((x) >= fLastByteAckd && (x) <= fNextByteToSend))
#define TCP_IS_GOOD_SEQ(x,y) (fNextByteToRead < fNextByteToRead + TCP_MAX_RECV_BUF ? \
(x) >= fNextByteToRead && (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF : \
((x) >= fNextByteToRead || (x) <= fNextByteToRead + TCP_MAX_RECV_BUF) && \
((x) + (y) >= fNextByteToRead || (x) + (y) <= fNextByteToRead + TCP_MAX_RECV_BUF))
struct tcp_segment {
struct list_link link;
@ -121,6 +112,33 @@ TCPConnection::~TCPConnection()
}
inline bool
TCPConnection::_IsAcknowledgeValid(uint32 acknowledge)
{
return fLastByteAckd > fNextByteToSend
? acknowledge >= fLastByteAckd || acknowledge <= fNextByteToSend
: acknowledge >= fLastByteAckd && acknowledge <= fNextByteToSend;
}
inline bool
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;
}
// integer overflow
return (sequence >= fNextByteToRead
|| sequence <= fNextByteToRead + TCP_MAX_RECV_BUF)
&& (end >= fNextByteToRead
|| end <= fNextByteToRead + TCP_MAX_RECV_BUF);
}
status_t
TCPConnection::Open()
{
@ -143,21 +161,23 @@ TCPConnection::Close()
{
BenaphoreLocker lock(&fLock);
TRACE(("TCP:%p.Close()\n", this));
if (fState == SYN_SENT || fState == LISTEN) {
if (fState == SYNCHRONIZE_SENT || fState == LISTEN) {
fState = CLOSED;
return B_OK;
}
tcp_state nextState = CLOSED;
if (fState == SYN_RCVD || fState == ESTABLISHED)
nextState = FIN_WAIT1;
if (fState == CLOSE_WAIT)
nextState = LAST_ACK;
status_t status = _SendQueuedData(TCP_FLG_FIN | TCP_FLG_ACK, false);
if (fState == SYNCHRONIZE_RECEIVED || fState == ESTABLISHED)
nextState = FINISH_SENT;
if (fState == FINISH_RECEIVED)
nextState = WAIT_FOR_FINISH_ACKNOWLEDGE;
status_t status = _SendQueuedData(TCP_FLAG_FINISH | TCP_FLAG_ACKNOWLEDGE, false);
if (status != B_OK)
return status;
fState = nextState;
TRACE(("TCP: %p.Close(): Entering state %d\n", this, fState));
//do i need to wait until fState returns to CLOSED?
// TODO: do i need to wait until fState returns to CLOSED?
return B_OK;
}
@ -180,7 +200,8 @@ TCPConnection::Free()
/*!
Creates and sends a SYN packet to /a address
Creates and sends a synchronize packet to /a address, and then waits
until the connection has been established or refused.
*/
status_t
TCPConnection::Connect(const struct sockaddr *address)
@ -191,7 +212,8 @@ 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
benaphore_lock(&gConnectionLock);
// want to release lock later, so no autolock
BenaphoreLocker lock(&fLock);
// Can only call Connect from CLOSED or LISTEN states
@ -203,6 +225,7 @@ TCPConnection::Connect(const struct sockaddr *address)
TRACE(("TCP: Connect(): in state %d\n", fState));
// get a net_route if there isn't one
// TODO: get a net_route_info instead!
if (fRoute == NULL) {
fRoute = gDatalinkModule->get_route(gDomain, (sockaddr *)address);
TRACE(("TCP: Connect(): Using Route %p\n", fRoute));
@ -245,12 +268,13 @@ TCPConnection::Connect(const struct sockaddr *address)
TRACE(("TCP: Connect(): starting 3-way handshake...\n"));
// send SYN
status = _SendQueuedData(TCP_FLG_SYN, false);
status = _SendQueuedData(TCP_FLAG_SYNCHRONIZE, false);
if (status != B_OK)
return status;
fState = SYN_SENT;
// TODO: Should Connect() not return until 3-way handshake is complete?
fState = SYNCHRONIZE_SENT;
// TODO: wait until 3-way handshake is complete
TRACE(("TCP: Connect(): Connection complete\n"));
return B_OK;
}
@ -356,7 +380,7 @@ TCPConnection::SendData(net_buffer *buffer)
fSendBuffer = buffer;
fNextByteToWrite += bufferSize;
return _SendQueuedData(TCP_FLG_ACK, false);
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, false);
}
@ -392,8 +416,8 @@ TCPConnection::ReadData(size_t numBytes, uint32 flags, net_buffer** _buffer)
BenaphoreLocker lock(&fLock);
// must be in a synchronous state
if (fState != ESTABLISHED || fState != FIN_WAIT1 || fState != FIN_WAIT2) {
// is this correct semantics?
if (fState != ESTABLISHED || fState != FINISH_SENT || fState != FINISH_ACKNOWLEDGED) {
// TODO: is this correct semantics?
dprintf(" TCP state = %d\n", fState);
return B_ERROR;
}
@ -520,17 +544,17 @@ TCPConnection::ReceiveData(net_buffer *buffer)
uint32 byteAckd = ntohl(header.acknowledge_num);
uint32 byteRcvd = ntohl(header.sequence_num);
uint32 headerLength = (uint32)header.header_length << 2;
uint32 payloadLength = buffer->size - headerLength;
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_FLG_ACK)
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
return _Reset(byteAckd, 0);
return _Reset(0, byteRcvd + payloadLength);
return _Reset(0, byteRcvd + dataLength);
case LISTEN:
// if packet is SYN, spawn new TCPConnection in SYN_RCVD state
@ -542,33 +566,33 @@ TCPConnection::ReceiveData(net_buffer *buffer)
// The current TCPConnection always remains in LISTEN state.
return B_ERROR;
case SYN_SENT:
if (header.flags & TCP_FLG_RST) {
case SYNCHRONIZE_SENT:
if (header.flags & TCP_FLAG_RESET) {
fError = ECONNREFUSED;
fState = CLOSED;
return B_ERROR;
}
if (header.flags & TCP_FLG_ACK && !TCP_IS_GOOD_ACK(byteAckd))
if (header.flags & TCP_FLAG_ACKNOWLEDGE && !_IsAcknowledgeValid(byteAckd))
return _Reset(byteAckd, 0);
if (header.flags & TCP_FLG_SYN) {
if (header.flags & TCP_FLAG_SYNCHRONIZE) {
fNextByteToRead = fNextByteExpected = ntohl(header.sequence_num) + 1;
flags |= TCP_FLG_ACK;
flags |= TCP_FLAG_ACKNOWLEDGE;
fLastByteAckd = byteAckd;
// cancel resend of this segment
if (header.flags & TCP_FLG_ACK)
if (header.flags & TCP_FLAG_ACKNOWLEDGE)
nextState = ESTABLISHED;
else {
nextState = SYN_RCVD;
flags |= TCP_FLG_SYN;
nextState = SYNCHRONIZE_RECEIVED;
flags |= TCP_FLAG_SYNCHRONIZE;
}
}
break;
case SYN_RCVD:
if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd))
case SYNCHRONIZE_RECEIVED:
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd))
fState = ESTABLISHED;
else
_Reset(byteAckd, 0);
@ -577,18 +601,18 @@ TCPConnection::ReceiveData(net_buffer *buffer)
default:
// In a synchronized state.
// first check that the received sequence number is good
if (TCP_IS_GOOD_SEQ(byteRcvd, payloadLength)) {
if (_IsSequenceValid(byteRcvd, dataLength)) {
// If a valid RST was received, terminate the connection.
if (header.flags & TCP_FLG_RST) {
if (header.flags & TCP_FLAG_RESET) {
fError = ECONNREFUSED;
fState = CLOSED;
return B_ERROR;
}
if (header.flags & TCP_FLG_ACK && TCP_IS_GOOD_ACK(byteAckd) ) {
if (header.flags & TCP_FLAG_ACKNOWLEDGE && _IsAcknowledgeValid(byteAckd)) {
fLastByteAckd = byteAckd;
if (fLastByteAckd == fNextByteToWrite) {
if (fState == LAST_ACK ) {
if (fState == WAIT_FOR_FINISH_ACKNOWLEDGE) {
nextState = CLOSED;
status = hash_remove(gConnectionHash, this);
if (status != B_OK)
@ -600,23 +624,23 @@ TCPConnection::ReceiveData(net_buffer *buffer)
if (status != B_OK)
return status;
}
if (fState == FIN_WAIT1) {
nextState = FIN_WAIT2;
if (fState == FINISH_SENT) {
nextState = FINISH_ACKNOWLEDGED;
}
}
}
if (header.flags & TCP_FLG_FIN) {
// other side is closing connection. change states
if (header.flags & TCP_FLAG_FINISH) {
// other side is closing connection; change states
switch (fState) {
case ESTABLISHED:
nextState = CLOSE_WAIT;
nextState = FINISH_RECEIVED;
fNextByteExpected++;
break;
case FIN_WAIT2:
case FINISH_ACKNOWLEDGED:
nextState = TIME_WAIT;
fNextByteExpected++;
break;
case FIN_WAIT1:
case FINISH_RECEIVED:
if (fLastByteAckd == fNextByteToWrite) {
// our FIN has been ACKd: go to TIME_WAIT
nextState = TIME_WAIT;
@ -632,10 +656,10 @@ TCPConnection::ReceiveData(net_buffer *buffer)
break;
}
}
flags |= TCP_FLG_ACK;
flags |= TCP_FLAG_ACKNOWLEDGE;
} else {
// out-of-order packet received. remind the other side of where we are
return _SendQueuedData(TCP_FLG_ACK, true);
return _SendQueuedData(TCP_FLAG_ACKNOWLEDGE, true);
}
break;
}
@ -653,7 +677,7 @@ TCPConnection::ReceiveData(net_buffer *buffer)
} else
gBufferModule->free(buffer);
if (fState != CLOSING && fState != LAST_ACK) {
if (fState != CLOSING && fState != WAIT_FOR_FINISH_ACKNOWLEDGE) {
status = _SendQueuedData(flags, false);
if (status != B_OK)
return status;
@ -676,7 +700,8 @@ TCPConnection::_Reset(uint32 sequence, uint32 acknowledge)
gAddressModule->set_to((sockaddr *)&reply->destination, (sockaddr *)&socket->peer);
status_t status = add_tcp_header(reply,
TCP_FLG_RST | (acknowledge == 0 ? 0 : TCP_FLG_ACK), sequence, acknowledge, 0);
TCP_FLAG_RESET | (acknowledge == 0 ? 0 : TCP_FLAG_ACKNOWLEDGE),
sequence, acknowledge, 0);
if (status != B_OK) {
gBufferModule->free(reply);
return status;
@ -758,11 +783,15 @@ TCPConnection::_SendQueuedData(uint16 flags, bool empty)
}
// Only count 1 SYN, the 1 sent when transitioning from CLOSED or LISTEN
if (TCP_FLG_SYN & flags && (fState == CLOSED || fState == LISTEN))
if ((flags & TCP_FLAG_SYNCHRONIZE) != 0 && (fState == CLOSED || fState == LISTEN))
fNextByteToSend++;
// Only count 1 FIN, the 1 sent when transitioning from ESTABLISHED, SYN_RCVD or CLOSE_WAIT
if (TCP_FLG_FIN & flags && (fState == SYN_RCVD || fState == ESTABLISHED || fState == CLOSE_WAIT))
// 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++;
fNextByteToSend += size;
#if 0

View File

@ -46,6 +46,9 @@ class TCPConnection : public net_protocol {
static int32 HashOffset() { return offsetof(TCPConnection, fHashLink); }
private:
bool _IsAcknowledgeValid(uint32 acknowledge);
bool _IsSequenceValid(uint32 sequence, uint32 length);
status_t _SendQueuedData(uint16 flags, bool empty);
status_t _EnqueueReceivedData(net_buffer *buffer, uint32 sequenceNumber);
status_t _Reset(uint32 sequenceNum, uint32 acknowledgeNum);

View File

@ -336,8 +336,8 @@ tcp_receive_data(net_buffer *buffer)
then check when local = peer = IPADDR_ANY.
port numbers always remain the same */
// If no connection exists (and RST is not set) send RST
if (!(header.flags & TCP_FLG_RST)) {
// If no connection exists (and RESET is not set) send RESET
if ((header.flags & TCP_FLAG_RESET) == 0) {
TRACE(("TCP: Connection does not exist!\n"));
net_buffer *reply = gBufferModule->create(512);
if (reply == NULL)
@ -350,15 +350,15 @@ tcp_receive_data(net_buffer *buffer)
uint32 sequence, acknowledge;
uint16 flags;
if (header.flags & TCP_FLG_ACK) {
if (header.flags & TCP_FLAG_ACKNOWLEDGE) {
sequence = ntohl(header.acknowledge_num);
acknowledge = 0;
flags = TCP_FLG_RST;
flags = TCP_FLAG_RESET;
} else {
sequence = 0;
acknowledge = ntohl(header.sequence_num) + 1
+ buffer->size - ((uint32)header.header_length << 2);
flags = TCP_FLG_RST | TCP_FLG_ACK;
flags = TCP_FLAG_RESET | TCP_FLAG_ACKNOWLEDGE;
}
status_t status = add_tcp_header(reply, flags, sequence, acknowledge, 0);

View File

@ -19,15 +19,20 @@
typedef enum {
// establishing a connection
CLOSED,
LISTEN,
SYN_SENT,
SYN_RCVD,
SYNCHRONIZE_SENT,
SYNCHRONIZE_RECEIVED,
ESTABLISHED,
CLOSE_WAIT,
LAST_ACK,
FIN_WAIT1,
FIN_WAIT2,
// peer closes the connection
FINISH_RECEIVED,
WAIT_FOR_FINISH_ACKNOWLEDGE,
// we close the connection
FINISH_SENT,
FINISH_ACKNOWLEDGED,
CLOSING,
TIME_WAIT
} tcp_state;
@ -53,14 +58,14 @@ struct tcp_header {
};
// TCP flag constants
#define TCP_FLG_CWR 0x80 // Congestion Window Reduced
#define TCP_FLG_ECN 0x40 // Explicit Congestion Notification echo
#define TCP_FLG_URG 0x20 // URGent
#define TCP_FLG_ACK 0x10 // ACKnowledge
#define TCP_FLG_PUS 0x08 // PUSh
#define TCP_FLG_RST 0x04 // ReSeT
#define TCP_FLG_SYN 0x02 // SYNchronize
#define TCP_FLG_FIN 0x01 // FINish
#define TCP_FLAG_FINISH 0x01
#define TCP_FLAG_SYNCHRONIZE 0x02
#define TCP_FLAG_RESET 0x04
#define TCP_FLAG_PUSH 0x08
#define TCP_FLAG_ACKNOWLEDGE 0x10
#define TCP_FLAG_URGENT 0x20
#define TCP_FLAG_ECN 0x40 // Explicit Congestion Notification echo
#define TCP_FLAG_CWR 0x80 // Congestion Window Reduced
struct tcp_connection_key {
const sockaddr *local;
@ -80,4 +85,4 @@ extern benaphore gConnectionLock;
status_t add_tcp_header(net_buffer *buffer, uint16 flags, uint32 sequence,
uint32 ack, uint16 advertisedWindow);
#endif TCP_H
#endif // TCP_H