* 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:
parent
088a32925d
commit
31d3ef17d8
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user