mirror of
https://github.com/ipxe/ipxe
synced 2026-02-14 02:31:26 +03:00
Fixed a bug in the TCP state machine. Added a transition from ESTABLISHED to LAST_ACK on receiving a FIN,ACK
This commit is contained in:
105
src/net/tcp.c
105
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_lstate = conn->tcp_state;
|
||||||
conn->tcp_state = nxt_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 */
|
/* TODO: Check if this check is required */
|
||||||
if ( conn->tcp_lstate == conn->tcp_state ||
|
if ( conn->tcp_lstate == conn->tcp_state ||
|
||||||
conn->tcp_state == TCP_INVALID ) {
|
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 ) );
|
tcphdr->csum = tcpip_chksum ( pkb->data, pkb_len ( pkb ) );
|
||||||
|
|
||||||
/* Dump the TCP header */
|
/* Dump the TCP header */
|
||||||
|
tcp_dump ( tcphdr );
|
||||||
|
|
||||||
/* Start the timer */
|
/* Start the timer */
|
||||||
if ( ( conn->tcp_state == TCP_ESTABLISHED && conn->tcp_lstate == TCP_SYN_SENT ) ||
|
if ( ( conn->tcp_state == TCP_ESTABLISHED && conn->tcp_lstate == TCP_SYN_SENT ) ||
|
||||||
@@ -719,23 +722,21 @@ static int tcp_rx ( struct pk_buff *pkb,
|
|||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Process TCP header */
|
/* Process TCP header */
|
||||||
tcphdr = pkb->data;
|
tcphdr = pkb->data;
|
||||||
tcp_dump ( tcphdr );
|
tcp_dump ( tcphdr );
|
||||||
|
|
||||||
/* Verify header length */
|
/* Verify header length */
|
||||||
hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4;
|
hlen = ( ( tcphdr->hlen & TCP_MASK_HLEN ) / 16 ) * 4;
|
||||||
if ( hlen != sizeof ( *tcphdr ) ) {
|
if ( hlen < sizeof ( *tcphdr ) ) {
|
||||||
if ( hlen == sizeof ( *tcphdr ) + 4 ) {
|
DBG ( "Bad header length (%d bytes)\n", hlen );
|
||||||
DBG ( "TCP options sent\n" );
|
rc = -EINVAL;
|
||||||
} else {
|
goto done;
|
||||||
DBG ( "Bad header length (%d bytes)\n", hlen );
|
}
|
||||||
rc = -EINVAL;
|
/* TODO: Parse TCP options */
|
||||||
goto done;
|
if ( hlen != sizeof ( *tcphdr ) ) {
|
||||||
}
|
DBG ( "Ignoring TCP options\n" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* TODO: Verify checksum */
|
/* TODO: Verify checksum */
|
||||||
|
|
||||||
@@ -828,16 +829,19 @@ static int tcp_rx ( struct pk_buff *pkb,
|
|||||||
/* Unexpected packet */
|
/* Unexpected packet */
|
||||||
goto unexpected;
|
goto unexpected;
|
||||||
case TCP_ESTABLISHED:
|
case TCP_ESTABLISHED:
|
||||||
#if 0
|
|
||||||
if ( tcphdr->flags & TCP_FIN ) {
|
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 */
|
/* FIN consumes one byte */
|
||||||
conn->rcv_nxt++;
|
conn->rcv_nxt++;
|
||||||
conn->tcp_flags |= TCP_ACK;
|
conn->tcp_flags |= TCP_ACK;
|
||||||
/* Send an acknowledgement */
|
/* Send the packet */
|
||||||
goto send_tcp_nomsg;
|
goto send_tcp_nomsg;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
/* Packet might contain data */
|
/* Packet might contain data */
|
||||||
break;
|
break;
|
||||||
case TCP_FIN_WAIT_1:
|
case TCP_FIN_WAIT_1:
|
||||||
@@ -901,60 +905,45 @@ static int tcp_rx ( struct pk_buff *pkb,
|
|||||||
assert ( ( tcphdr->flags & TCP_ACK ) ||
|
assert ( ( tcphdr->flags & TCP_ACK ) ||
|
||||||
pkb_len ( pkb ) > sizeof ( *tcphdr ) );
|
pkb_len ( pkb ) > sizeof ( *tcphdr ) );
|
||||||
|
|
||||||
/* Check for new data */
|
/**
|
||||||
toack = pkb_len ( pkb ) - hlen;
|
* Check if the received packet ACKs sent data
|
||||||
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 */
|
|
||||||
if ( tcphdr->flags & TCP_ACK ) {
|
if ( tcphdr->flags & TCP_ACK ) {
|
||||||
acked = ntohl ( tcphdr->ack ) - conn->snd_una;
|
acked = ntohl ( tcphdr->ack ) - conn->snd_una;
|
||||||
if ( acked < 0 ) { /* TODO: Replace all uint32_t arith */
|
if ( acked < 0 ) {
|
||||||
DBG ( "Previously ACKed (%d)\n", tcphdr->ack );
|
/* Packet ACKs previously ACKed data */
|
||||||
|
DBG ( "Previously ACKed data %lx\n",
|
||||||
|
ntohl ( tcphdr->ack ) );
|
||||||
rc = 0;
|
rc = 0;
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Advance snd stream */
|
/* Invoke the acked() callback */
|
||||||
conn->snd_una += acked;
|
conn->snd_una += acked;
|
||||||
/* Invoke the acked() callback function */
|
|
||||||
conn->tcp_op->acked ( conn, acked );
|
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 ) ) {
|
* Check if packet contains new data
|
||||||
if ( tcphdr->flags & TCP_ACK ) {
|
*/
|
||||||
tcp_trans ( conn, TCP_LAST_ACK );
|
toack = pkb_len ( pkb ) - hlen;
|
||||||
goto send_tcp_nomsg;
|
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 );
|
||||||
}
|
}
|
||||||
tcp_trans ( conn, TCP_CLOSE_WAIT );
|
conn->tcp_flags |= TCP_ACK;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
/* Otherwise, the packet has been ACKed already */
|
|
||||||
}
|
}
|
||||||
rc = 0;
|
|
||||||
goto done;
|
/**
|
||||||
|
* Send data
|
||||||
|
*/
|
||||||
|
tcp_senddata ( conn );
|
||||||
|
return 0;
|
||||||
|
|
||||||
send_tcp_nomsg:
|
send_tcp_nomsg:
|
||||||
free_pkb ( conn->tx_pkb );
|
free_pkb ( conn->tx_pkb );
|
||||||
|
|||||||
Reference in New Issue
Block a user