summaryrefslogtreecommitdiffstats
path: root/src/net/tcp.c
diff options
context:
space:
mode:
authorNikhil Chandru Rao2006-08-19 18:14:53 +0200
committerNikhil Chandru Rao2006-08-19 18:14:53 +0200
commitdccb8358bd3e5d5be3b3cf1e054dce76549a1efe (patch)
tree8fcf68fd0a2344efd5eedb0a2f9681ae45160079 /src/net/tcp.c
parentIP6 support (diff)
downloadipxe-dccb8358bd3e5d5be3b3cf1e054dce76549a1efe.tar.gz
ipxe-dccb8358bd3e5d5be3b3cf1e054dce76549a1efe.tar.xz
ipxe-dccb8358bd3e5d5be3b3cf1e054dce76549a1efe.zip
Fixed a bug in the TCP state machine. Added a transition from ESTABLISHED to LAST_ACK on receiving a FIN,ACK
Diffstat (limited to 'src/net/tcp.c')
-rw-r--r--src/net/tcp.c103
1 files changed, 46 insertions, 57 deletions
diff --git a/src/net/tcp.c b/src/net/tcp.c
index 097b77d9..b84c302b 100644
--- a/src/net/tcp.c
+++ b/src/net/tcp.c
@@ -329,6 +329,8 @@ void tcp_trans ( struct tcp_connection *conn, int nxt_state ) {
conn->tcp_lstate = conn->tcp_state;
conn->tcp_state = nxt_state;
+ DBG ( "Transition from %s to %s\n", tcp_states[conn->tcp_lstate], tcp_states[conn->tcp_state] );
+
/* TODO: Check if this check is required */
if ( conn->tcp_lstate == conn->tcp_state ||
conn->tcp_state == TCP_INVALID ) {
@@ -682,6 +684,7 @@ int tcp_send ( struct tcp_connection *conn, const void *data, size_t len ) {
tcphdr->csum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
/* Dump the TCP header */
+ tcp_dump ( tcphdr );
/* Start the timer */
if ( ( conn->tcp_state == TCP_ESTABLISHED && conn->tcp_lstate == TCP_SYN_SENT ) ||
@@ -719,24 +722,22 @@ static int tcp_rx ( struct pk_buff *pkb,
goto done;
}
-
/* Process TCP header */
tcphdr = pkb->data;
tcp_dump ( tcphdr );
/* Verify header length */
hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4;
+ if ( hlen < sizeof ( *tcphdr ) ) {
+ DBG ( "Bad header length (%d bytes)\n", hlen );
+ rc = -EINVAL;
+ goto done;
+ }
+ /* TODO: Parse TCP options */
if ( hlen != sizeof ( *tcphdr ) ) {
- if ( hlen == sizeof ( *tcphdr ) + 4 ) {
- DBG ( "TCP options sent\n" );
- } else {
- DBG ( "Bad header length (%d bytes)\n", hlen );
- rc = -EINVAL;
- goto done;
- }
+ DBG ( "Ignoring TCP options\n" );
}
-
/* TODO: Verify checksum */
/* Demux TCP connection */
@@ -828,16 +829,19 @@ static int tcp_rx ( struct pk_buff *pkb,
/* Unexpected packet */
goto unexpected;
case TCP_ESTABLISHED:
-#if 0
if ( tcphdr->flags & TCP_FIN ) {
- tcp_trans ( conn, TCP_CLOSE_WAIT );
+ if ( tcphdr->flags & TCP_ACK ) {
+ tcp_trans ( conn, TCP_LAST_ACK );
+ conn->tcp_flags |= TCP_FIN;
+ } else {
+ tcp_trans ( conn, TCP_CLOSE_WAIT );
+ }
/* FIN consumes one byte */
conn->rcv_nxt++;
conn->tcp_flags |= TCP_ACK;
- /* Send an acknowledgement */
+ /* Send the packet */
goto send_tcp_nomsg;
}
-#endif
/* Packet might contain data */
break;
case TCP_FIN_WAIT_1:
@@ -901,60 +905,45 @@ static int tcp_rx ( struct pk_buff *pkb,
assert ( ( tcphdr->flags & TCP_ACK ) ||
pkb_len ( pkb ) > sizeof ( *tcphdr ) );
- /* Check for new data */
- toack = pkb_len ( pkb ) - hlen;
- if ( toack > 0 ) {
- /* Check if expected sequence number */
- if ( conn->rcv_nxt == ntohl ( tcphdr->seq ) ) {
- conn->rcv_nxt += toack;
- conn->tcp_op->newdata ( conn, pkb->data + hlen,
- toack );
- } else {
- DBG ( "Unexpected sequence number %lx (wanted %lx)\n",
- ntohl ( tcphdr->seq ), conn->rcv_nxt );
- }
-
- /* Acknowledge new data */
- conn->tcp_flags |= TCP_ACK;
- if ( !( tcphdr->flags & TCP_ACK ) ) {
- goto send_tcp_nomsg;
- }
- }
-
- /* Process ACK */
+ /**
+ * Check if the received packet ACKs sent data
+ */
if ( tcphdr->flags & TCP_ACK ) {
acked = ntohl ( tcphdr->ack ) - conn->snd_una;
- if ( acked < 0 ) { /* TODO: Replace all uint32_t arith */
- DBG ( "Previously ACKed (%d)\n", tcphdr->ack );
+ if ( acked < 0 ) {
+ /* Packet ACKs previously ACKed data */
+ DBG ( "Previously ACKed data %lx\n",
+ ntohl ( tcphdr->ack ) );
rc = 0;
goto done;
}
- /* Advance snd stream */
+ /* Invoke the acked() callback */
conn->snd_una += acked;
- /* Invoke the acked() callback function */
conn->tcp_op->acked ( conn, acked );
- /* Invoke the senddata() callback function */
- tcp_senddata ( conn );
}
-
- /* If the connection is in ESTABLISHED and it receives a FIN */
- if ( conn->tcp_state == ESTABLISHED && ( tcphdr->flags & TCP_FIN ) ) {
- if ( tcphdr->flags & TCP_ACK ) {
- tcp_trans ( conn, TCP_LAST_ACK );
- goto send_tcp_nomsg;
- }
- tcp_trans ( conn, TCP_CLOSE_WAIT );
-// conn->tcp_op->closed ( conn, CONN_SNDCLOSE );
- conn->rcv_nxt++;
- if ( ! ( tcphdr->flags & TCP_ACK ) ) {
- conn->tcp_flags |= TCP_ACK;
- /* Send an acknowledgement */
- goto send_tcp_nomsg;
+
+ /**
+ * Check if packet contains new data
+ */
+ toack = pkb_len ( pkb ) - hlen;
+ if ( toack >= 0 ) {
+ /* Check the sequence number */
+ if ( conn->rcv_nxt == ntohl ( tcphdr->seq ) ) {
+ conn->rcv_nxt += toack;
+ conn->tcp_op->newdata ( conn,
+ pkb->data + hlen, toack );
+ } else {
+ DBG ( "Unexpected sequence number %lx (wanted %lx)\n",
+ ntohl ( tcphdr->ack ), conn->rcv_nxt );
}
- /* Otherwise, the packet has been ACKed already */
+ conn->tcp_flags |= TCP_ACK;
}
- rc = 0;
- goto done;
+
+ /**
+ * Send data
+ */
+ tcp_senddata ( conn );
+ return 0;
send_tcp_nomsg:
free_pkb ( conn->tx_pkb );