Skip to content

Instantly share code, notes, and snippets.

@sclaxton
Created March 3, 2014 13:58
Show Gist options
  • Select an option

  • Save sclaxton/9325435 to your computer and use it in GitHub Desktop.

Select an option

Save sclaxton/9325435 to your computer and use it in GitHub Desktop.
Implementation of TCP state handling for an undergraduate networks class at the University of Chicago.
/*
* chiTCP - A simple, testable TCP stack
*
* Implementation of the TCP protocol.
*
* chiTCP follows a state machine approach to implementing TCP.
* This means that there is a handler function for each of
* the TCP states (CLOSED, LISTEN, SYN_RCVD, etc.). If an
* event (e.g., a packet arrives) while the connection is
* in a specific state (e.g., ESTABLISHED), then the handler
* function for that state is called, along with information
* about the event that just happened.
*
* Each handler function has the following prototype:
*
* int f(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event);
*
* si is a pointer to the chiTCP server info. The functions in
* this file will not have to access the data in the server info,
* but this pointer is needed to call other functions.
*
* entry is a pointer to the socket entry for the connection that
* is being handled. The socket entry contains the actual TCP
* data (variables, buffers, etc.), which can be extracted
* like this:
*
* tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
*
* other than that, no other fields in "entry" should be read
* or modified.
*
* event is the event that has caused the TCP thread to wake up. The
* list of possible events corresponds roughly to the ones
* specified in http://tools.ietf.org/html/rfc793#section-3.9.
* They are:
*
* APPLICATION_CONNECT: Application has called socket_connect()
* and a three-way handshake must be initiated.
*
* APPLICATION_SEND: Application has called socket_send() and
* there is unsent data in the send buffer.
*
* APPLICATION_RECEIVE: Application has called socket_recv() and
* any received-and-acked data in the recv buffer will be
* collected by the application (up to the maximum specified
* when calling socket_recv).
*
* APPLICATION_CLOSE: Application has called socket_close() and
* a connection tear-down should be initiated.
*
* PACKET_ARRIVAL: A packet has arrived through the network and
* needs to be processed (RFC 793 calls this "SEGMENT ARRIVES")
*
* TIMEOUT: A timeout (e.g., a retransmission timeout) has
* happened.
*
*/
/*
* Copyright (c) 2013-2014, The University of Chicago
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or withsend
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of The University of Chicago nor the names of its
* contributors may be used to endorse or promote products derived from this
* software withsend specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY send OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "chitcp/log.h"
#include "chitcp/buffer.h"
#include "serverinfo.h"
#include "connection.h"
#include "tcp.h"
#include "src/simclist/simclist.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <assert.h>
#include <netinet/in.h>
int chitcpd_tcp_state_handle_CLOSED(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == APPLICATION_CONNECT)
{
/* We assume it is an active open, so we need to send SYN
* and enter the SYN_SENT state.
*/
chilog(INFO,"handling APPLICATION_CONNECT from state CLOSED");
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
/* Randomly choose initial ISN */
srand(time(NULL));
tcp_seq ISS = (tcp_seq) rand() % 10000;
/*set tcp vars*/
tcp_data->ISS = ISS;
chilog(INFO,"ISS initialized to %d", ISS);
circular_buffer_init(&tcp_data->recv, TCP_BUFFER_SIZE);
circular_buffer_init(&tcp_data->send, TCP_BUFFER_SIZE);
tcp_data->RCV_WND = circular_buffer_available(&tcp_data->recv);
chilog(INFO,"RCV.WND set to %d", tcp_data->RCV_WND);
tcp_data->SND_WND = 0;
chilog(INFO,"SND.WND initialized to %d", tcp_data->SND_WND);
/* Set SND.UNA and SND.NXT. */
tcp_data->SND_UNA = ISS;
chilog(INFO,"SND.UNA set to %d", tcp_data->SND_UNA);
tcp_data->SND_NXT = ISS+1;
chilog(INFO,"SND.NXT set to %d", tcp_data->SND_NXT);
/*prepare packet to be sent to server*/
tcp_packet_t *packet = malloc(sizeof(tcp_packet_t));
/* Create packet with no payload. */
chitcpd_tcp_packet_create(entry, packet, NULL, 0);
tcphdr_t *header = TCP_PACKET_HEADER(packet);
header->seq = htonl(ISS);
header->win = htons(get_advertised_window(tcp_data));
header->syn = 1;
/* Send packet and return. */
chitcpd_send_tcp_packet(si, entry, packet);
chilog(INFO,"sending packet:");
chilog_tcp(INFO,packet,LOG_OUTBOUND);
/* Change state to SYN_SENT. */
chitcpd_update_tcp_state(si, entry, SYN_SENT);
chilog(INFO,"set state to SYN_SENT");
return CHITCP_OK;
}
else if (event == CLEANUP)
{
/* Any additional cleanup goes here */
}
else
chilog(WARNING, "In CLOSED state, received unexpected event.");
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_LISTEN(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
/* Get packet off queue. */
tcp_packet_t *in_packet;
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
pthread_mutex_lock(&tcp_data->lock_pending_packets);
in_packet = list_fetch(&tcp_data->pending_packets);
pthread_mutex_unlock(&tcp_data->lock_pending_packets);
tcphdr_t *in_header = TCP_PACKET_HEADER(in_packet);
/*declare reply packet*/
tcp_packet_t *out_packet;
tcphdr_t *out_header;
if (in_header->rst) {
/* Ignore resets. */
} else if (in_header->ack) {
/*ignore resets*/
} else if (in_header->syn) {
/* We need to send syn/ack. */
int SEG_SEQ = SEG_SEQ(in_packet);
/* Set RCV_NXT and IRS. */
SET_AND_LOG(tcp_data->RCV_NXT, SEG_SEQ+1);
SET_AND_LOG(tcp_data->IRS, SEG_SEQ);
/* Generate our ISS, sent SND.UNA and SND.NXT */
srand(time(NULL));
tcp_seq ISS = (tcp_seq) rand() % 10000;
/*set tcp vars*/
SET_AND_LOG(tcp_data->ISS, ISS);
circular_buffer_init(&tcp_data->recv, TCP_BUFFER_SIZE);
circular_buffer_init(&tcp_data->send, TCP_BUFFER_SIZE);
SET_AND_LOG(tcp_data->RCV_WND, circular_buffer_available(&tcp_data->recv));
SET_AND_LOG(tcp_data->SND_WND, 0);
/* Set SND.UNA and SND.NXT. */
SET_AND_LOG(tcp_data->SND_UNA, ISS);
SET_AND_LOG(tcp_data->SND_NXT, ISS+1);
/*prepare packet for reply*/
out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
out_header = TCP_PACKET_HEADER(out_packet);
out_header->seq = htonl(ISS);
out_header->win = htons(get_advertised_window(tcp_data));
out_header->ack_seq = htonl(tcp_data->RCV_NXT);
out_header->syn = 1;
out_header->ack = 1;
chitcpd_update_tcp_state(si, entry, SYN_RCVD);
chilog(INFO,"set state to SYN_RCVD");
chilog(INFO,"sending packet:");
chilog_tcp(INFO,out_packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
return CHITCP_OK;
} else {
chilog(WARNING, "Something is seriously borked up in state LISTEN.");
}
}
else
chilog(WARNING, "In LISTEN state, received unexpected event.");
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_SYN_RCVD(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
/* Get packet off queue. */
tcp_packet_t *in_packet;
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
pthread_mutex_lock(&tcp_data->lock_pending_packets);
in_packet = list_fetch(&tcp_data->pending_packets);
pthread_mutex_unlock(&tcp_data->lock_pending_packets);
tcphdr_t *in_header = TCP_PACKET_HEADER(in_packet);
/*if segment is acceptable*/
if ( seg_is_acceptable(in_packet, tcp_data) )
{
if (in_header->syn)
{
/*rfc793 says in this case to:*/
/*If the SYN is in the window it is an error, send a reset, any*/
/*outstanding RECEIVEs and SEND should receive "reset" responses,*/
/*all segment queues should be flushed, the user should also*/
/*receive an unsolicited general "connection reset" signal, enter*/
/*the CLOSED state, delete the TCB, and return.*/
/*ignore reset*/
/*ERROR: SYN recvd out of window. Enter state CLOSED*/
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(ERROR,"ERROR: SYN recvd out of window. Set state to CLOSED.");
return CHITCP_OK;
}
if (in_header->ack)
{
tcp_data->SND_UNA = SEG_ACK(in_packet);
/*enter state ESTABLISHED*/
SET_AND_LOG(tcp_data->SND_WND, SEG_WND(in_packet));
chilog(INFO,"Setting SND_WND to %d",tcp_data->SND_WND);
chitcpd_update_tcp_state(si, entry, ESTABLISHED);
chilog(INFO,"Set state to ESTABLISHED");
}
} else {
handle_seg_not_acceptable(si, entry, in_packet, tcp_data);
}
}
else
chilog(WARNING, "In SYN_RCVD state, received unexpected event.");
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_SYN_SENT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
/* Get packet off queue. */
tcp_packet_t *in_packet;
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
pthread_mutex_lock(&tcp_data->lock_pending_packets);
in_packet = list_fetch(&tcp_data->pending_packets);
pthread_mutex_unlock(&tcp_data->lock_pending_packets);
tcphdr_t *in_header = TCP_PACKET_HEADER(in_packet);
if (in_header->ack)
{
if ( ack_is_acceptable(in_packet, tcp_data) )
{
/*ACK is acceptable...move along*/
} else {
/*ignore unacceptable acks, drop packet and return*/
chilog(WARNING, "In SYN_SENT state, received unacceptable ACK.");
return CHITCP_OK;
}
}
/*rfc793 specifies handling of the rst bit here which we are skipping*/
/*rfc793 specifies handling of security/compartment and precedence here
which we assuming to be correct and valid*/
if (in_header->syn)
{
tcp_packet_t *out_packet;
tcphdr_t *out_header;
uint32_t SEG_SEQ = SEG_SEQ(in_packet);
chilog(INFO, "Setting RCV.NXT to %d.", SEG_SEQ+1);
tcp_data->RCV_NXT = SEG_SEQ + 1;
chilog(INFO, "Setting IRS to %d.", SEG_SEQ);
tcp_data->IRS = SEG_SEQ;
/*note: ack is acceptable if it exists*/
if (in_header->ack)
{
/*if retransmitting vacate retransmission queue from start index */
/*to SEG_ACK - SND_UNA - 1*/
uint32_t SEG_ACK = SEG_ACK(in_packet);
uint32_t SND_NXT = tcp_data->SND_NXT;
tcp_data->SND_UNA = SEG_ACK;
chilog(INFO, "Set SND.UNA to %d.", SEG_ACK);
uint32_t ISS = tcp_data->ISS;
uint32_t RCV_NXT = tcp_data->RCV_NXT;
/*if our SYN has been ACKed*/
if (SEG_ACK > ISS)
{
/*enter state ESTABLISHED*/
tcp_data->SND_WND = SEG_WND(in_packet);
chitcpd_update_tcp_state(si, entry, ESTABLISHED);
chilog(INFO,"set state to ESTABLISHED");
/* Get ready to send the ACK. */
out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
out_header = TCP_PACKET_HEADER(out_packet);
out_header->win = htons(get_advertised_window(tcp_data));
out_header->seq = htonl(SND_NXT);
out_header->ack_seq = htonl(RCV_NXT);
out_header->ack = 1;
chilog(INFO,"sending packet:");
chilog_tcp(INFO,out_packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
return CHITCP_OK;
} else {
/*enter state SYN-RCVD*/
chitcpd_update_tcp_state(si, entry, SYN_RCVD);
chilog(INFO,"set state to SYN_RCVD");
/* Get ready to send the SYN,ACK. */
out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
out_header = TCP_PACKET_HEADER(out_packet);
out_header->win = htons(get_advertised_window(tcp_data));
out_header->seq = htonl(ISS);
out_header->ack_seq = htonl(RCV_NXT);
out_header->ack = 1;
out_header->syn = 1;
chilog(INFO,"sending packet:");
chilog_tcp(INFO,out_packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
return CHITCP_OK;
}
}
} else {
/*no syn or acceptable ack do nothing*/
chilog(WARNING, "In SYN_SENT state, received no acceptable ACK or SYN.");
}
}
else {
chilog(WARNING, "In SYN_SENT state, received unexpected event.");
}
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_ESTABLISHED(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
if (event == APPLICATION_SEND)
{
process_send_buffer(si, entry, event);
return CHITCP_OK;
}
else if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, ESTABLISHED);
if (!packet) return CHITCP_OK;
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
/* HANDLE FIN */
if (in_header->fin) {
chitcpd_update_tcp_state(si, entry, CLOSE_WAIT);
chilog(INFO,"Set state to CLOSE_WAIT");
// close the recv buffer
circular_buffer_close(&tcp_data->recv);
//send_ack(si, entry, tcp_data);
}
chitcp_tcp_packet_free(packet);
}
else if (event == APPLICATION_RECEIVE)
{
chilog(INFO, "Handling event APPLICATION_RECEIVE");
/* get tcp data */
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
/* get the amount of space in the rcv buffer */
uint32_t NEW_RCV_WND = circular_buffer_available(&tcp_data->recv);
uint32_t OLD_RCV_WND = tcp_data->RCV_WND;
chilog(INFO, "updating receive window: RCV_WND [%d] --> [%d]", OLD_RCV_WND, NEW_RCV_WND);
/* set recv window to amount of buffer space */
SET_AND_LOG(tcp_data->RCV_WND, NEW_RCV_WND);
/*send an ACK of segment put into buffer, piggybacked or not*/
if (circular_buffer_count(&tcp_data->send)) {
process_send_buffer(si, entry, ESTABLISHED);
} else {
send_ack(si, entry, tcp_data);
}
}
else if (event == APPLICATION_CLOSE)
{
/* Set the closing flag so we send the FIN packet when we next SEND. */
tcp_data->closing = 1;
chitcpd_update_tcp_state(si, entry, FIN_WAIT_1);
if (!circular_buffer_count(&tcp_data->send)){
send_fin(si, entry, tcp_data);
circular_buffer_close(&tcp_data->send);
}
return CHITCP_OK;
}
else
chilog(WARNING, "In ESTABLISHED state, received unexpected event (%i).", event);
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_FIN_WAIT_1(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, FIN_WAIT_1);
if (!packet) return CHITCP_OK;
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
/* Handle ACK to our FIN */
if (in_header->ack && tcp_data->SND_UNA == tcp_data->SND_NXT){
chitcpd_update_tcp_state(si, entry, FIN_WAIT_2);
chilog(INFO,"Set state to FIN_WAIT_2");
}
/* Simultaneous close */
if (in_header->ack && in_header->fin){
chitcpd_update_tcp_state(si, entry, TIME_WAIT);
chilog(INFO,"Simultaneous close, entering TIME_WAIT ");
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(INFO,"Simultaneous close, entering CLOSING ");
send_ack(si, entry, tcp_data);
} else if (in_header->fin) {
send_ack(si, entry, tcp_data);
chitcpd_update_tcp_state(si, entry, CLOSING);
}
chitcp_tcp_packet_free(packet);
return CHITCP_OK;
}
else
chilog(WARNING, "In FIN_WAIT_1 state, received unexpected event (%i).", event);
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_FIN_WAIT_2(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, FIN_WAIT_2);
if (!packet) return CHITCP_OK;
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
/* HANDLE FIN */
if (in_header->fin) {
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
send_ack(si, entry, tcp_data);
chitcpd_update_tcp_state(si, entry, TIME_WAIT);
chilog(INFO,"Set state to TIME_WAIT");
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(INFO,"Simultaneous close, entering CLOSED ");
}
chitcp_tcp_packet_free(packet);
}
else
chilog(WARNING, "In FIN_WAIT_2 state, received unexpected event (%i).", event);
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_CLOSE_WAIT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == APPLICATION_CLOSE)
{
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
send_fin(si, entry, tcp_data);
chitcpd_update_tcp_state(si, entry, LAST_ACK);
chilog(INFO,"Set state to LAST_ACK");
}
else if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, CLOSE_WAIT);
if (packet) chitcp_tcp_packet_free(packet);
}
else
chilog(WARNING, "In CLOSE_WAIT state, received unexpected event (%i).", event);
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_CLOSING(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, CLOSING);
if (!packet) return CHITCP_OK;
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
/* Handle ACK to our FIN */
if (in_header->ack){
chitcpd_update_tcp_state(si, entry, TIME_WAIT);
chilog(INFO,"Set state to TIME_WAIT");
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(INFO,"Simultaneous close, entering CLOSED ");
}
/* HANDLE FIN */
if (in_header->fin) {
chilog(WARNING,"Got FIN in closing, set state to CLOSED anyway");
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(INFO,"Set state to CLOSED");
}
chitcp_tcp_packet_free(packet);
}
else
chilog(WARNING, "In CLOSING state, received unexpected event (%i).", event);
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_TIME_WAIT(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
chilog(WARNING, "Running handler for TIME_WAIT. This should not happen.");
return CHITCP_OK;
}
int chitcpd_tcp_state_handle_LAST_ACK(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
if (event == PACKET_ARRIVAL)
{
tcp_packet_t * packet = post_handshake_packet_accept(si, entry, event, FIN_WAIT_2);
if (!packet) return CHITCP_OK;
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
/* Handle ACK to our FIN */
if (in_header->ack){
chitcpd_update_tcp_state(si, entry, CLOSED);
chilog(INFO,"Set state to CLOSED");
}
chitcp_tcp_packet_free(packet);
}
else
chilog(WARNING, "In LAST_ACK state, received unexpected event (%i).", event);
return CHITCP_OK;
}
/* */
/* Any additional functions you need should go here */
/* */
int ack_is_acceptable(tcp_packet_t *packet, tcp_data_t *data)
{
/*check to see if ack is acceptable*/
uint32_t SND_UNA = data->SND_UNA;
uint32_t SND_NXT = data->SND_NXT;
uint32_t SEG_ACK = SEG_ACK(packet);
return SND_UNA < SEG_ACK && SEG_ACK <= SND_NXT;
}
int seg_is_acceptable(tcp_packet_t *packet, tcp_data_t *data)
{
uint32_t RCV_NXT = data->RCV_NXT;
uint32_t RCV_WND = data->RCV_WND;
uint32_t SEG_SEQ = SEG_SEQ(packet);
uint32_t SEG_LEN = SEG_LEN(packet);
return ((RCV_NXT <= SEG_SEQ) && (SEG_SEQ < RCV_NXT + RCV_WND)) ||
((RCV_NXT <= SEG_SEQ+SEG_LEN-1) && (SEG_SEQ+SEG_LEN-1 < RCV_NXT+RCV_WND));
}
void handle_seg_not_acceptable(serverinfo_t *si, chisocketentry_t *entry, tcp_packet_t *packet, tcp_data_t *data){
chilog(INFO,"Unacceptable segment received.");
tcphdr_t *in_header = TCP_PACKET_HEADER(packet);
if (in_header->ack)
{
/* rfc793 says to allow for acceptable ack that is in an unacceptable segment??? */
if ( ack_is_acceptable(packet, data) )
{
chilog(INFO,"Unacceptable segment received containing an acceptable ACK.");
data->SND_UNA = SEG_ACK(packet);
/*enter state ESTABLISHED*/
chitcpd_update_tcp_state(si, entry, ESTABLISHED);
chilog(INFO,"Set state to ESTABLISHED");
}
/*ignore unacceptable ACK*/
} else {
/* Get ready to send ACK in reply to unacceptable segment */
tcp_packet_t *out_packet;
tcphdr_t *out_header;
out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
out_header = TCP_PACKET_HEADER(out_packet);
out_header->seq = htonl(data->SND_NXT);
out_header->win = htons(get_advertised_window(data));
out_header->ack_seq = htonl(data->RCV_NXT);
out_header->ack = 1;
chilog(INFO,"sending packet:");
chilog_tcp(INFO,out_packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
/*return and drop the segment*/
}
}
/* Process the send buffer, sending all the data we can. */
int process_send_buffer(serverinfo_t *si, chisocketentry_t *entry, tcp_event_type_t event)
{
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
int max = (tcp_data->SND_UNA + tcp_data->SND_WND) - tcp_data->SND_NXT;
/* Now we need to segmentize the data in the buffer into some packets
* and send them.
*/
int read;
chilog(INFO, "Fetching packet of max size %d from buffer", max);
uint8_t tmp[TCP_MSS];
int to_read = TCP_MSS < max ? TCP_MSS : max;
chilog(DEBUG, "to_read value is %d", to_read);
while((max > 0) && ((read = circular_buffer_read(&tcp_data->send, tmp, to_read, 0)) != CHITCP_EWOULDBLOCK)) {
chilog(INFO, "Read %d bytes from buffer while building packet.", read);
send_packet(si, entry, tmp, read);
max -= read;
}
if (tcp_data->closing) {
/* Send FIN segment. */
tcp_packet_t *packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, packet, NULL, 0);
tcphdr_t *header = TCP_PACKET_HEADER(packet);
header->seq = htonl(tcp_data->SND_NXT);
header->ack_seq = htonl(tcp_data->RCV_NXT);
header->ack = 1;
header->win = htons(get_advertised_window(tcp_data));
chilog(INFO,"sending fin segment while procesing send buffer:");
chilog_tcp(INFO,packet,LOG_OUTBOUND);
chilog(INFO,"we should definitely not be sending any more packets after this");
chitcpd_send_tcp_packet(si, entry, packet);
// close the send buffer
circular_buffer_close(&tcp_data->send);
}
return max; // the amount of data we sent
// TODO: handle errors better?
}
/* Allocate the a packet we from the send buffer and send it.
* max is the maximum size of packet to send
Return the number of bytes we sent. */
int send_packet(serverinfo_t *si, chisocketentry_t *entry, uint8_t *data, int len)
{
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
tcp_packet_t *packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, packet, data, len);
/* Set all the header fields correctly. */
tcphdr_t *header = TCP_PACKET_HEADER(packet);
header->seq = htonl(tcp_data->SND_NXT);
header->ack_seq = htonl(tcp_data->RCV_NXT);
header->ack = 1;
header->win = htons(get_advertised_window(tcp_data));
chilog(INFO,"sending packet while procesing send buffer:");
chilog_tcp(INFO,packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, packet);
chitcp_tcp_packet_free(packet);
/* Update SND.NXT. */
tcp_data->SND_NXT += len;
chilog(INFO,"set SND_NXT to %d", tcp_data->SND_NXT);
return 0;
}
uint16_t get_advertised_window(tcp_data_t *data)
{
circular_buffer_t *recv_buff = &data->recv;
if (data->RCV_WND < (circular_buffer_capacity(recv_buff) / 4))
{
return 0;
} else {
return data->RCV_WND;
}
}
tcp_packet_t *post_handshake_packet_accept(serverinfo_t *si,
chisocketentry_t *entry,
tcp_event_type_t event,
int tcp_state)
{
/* Get packet off queue. */
tcp_packet_t *in_packet;
tcp_data_t *tcp_data = &entry->socket_state.active.tcp_data;
/*
* General header handling
*/
/* grab the packet from the pending packets queue */
pthread_mutex_lock(&tcp_data->lock_pending_packets);
in_packet = list_fetch(&tcp_data->pending_packets);
pthread_mutex_unlock(&tcp_data->lock_pending_packets);
/* LOG THAT THANG. */
chilog(DEBUG, "THIS IS WHAT THE PACKET WE ARE HANDLING LOOKS LIKE:");
chilog_tcp(DEBUG, in_packet, LOG_INBOUND);
tcphdr_t *in_header = TCP_PACKET_HEADER(in_packet);
/* CHECK IF ACCEPTABLE */
if (!seg_is_acceptable(in_packet, tcp_data)){
handle_seg_not_acceptable(si, entry, in_packet, tcp_data);
return NULL;
}
/* ERROR ON SYN */
if (in_header->syn) {
/* We should not receive a SYN after the handshake, this is an error */
chilog(ERROR,"ERROR: SYN recvd out of window. Setting state to CLOSED.");
chitcpd_update_tcp_state(si, entry, CLOSED);
return CHITCP_OK;
}
/* HANDLE ACK */
if (in_header->ack) {
if (ack_is_acceptable(in_packet, tcp_data)) {
SET_AND_LOG(tcp_data->SND_UNA, SEG_ACK(in_packet));
SET_AND_LOG(tcp_data->SND_WND, SEG_WND(in_packet));
} else if ( SEG_ACK(in_packet) > tcp_data->SND_NXT) {
send_ack(si, entry, tcp_data);
}
}
/*
* WRITE PAYLOAD TO RECV BUFFER
*/
size_t payload_len = TCP_PAYLOAD_LEN(in_packet);
SET_AND_LOG(tcp_data->RCV_NXT, SEG_SEQ(in_packet) + SEG_LEN(in_packet));
SET_AND_LOG(tcp_data->RCV_WND, circular_buffer_available(&tcp_data->recv));
if (payload_len > tcp_data->RCV_WND){
chilog(ERROR, "Packet received that we cannot handle. Buffer overflow: %d", payload_len);
return in_packet;
}
if (payload_len <= 0){
return in_packet;
}
/*write payload to recv buffer*/
uint8_t *payload = TCP_PAYLOAD_START(in_packet);
circular_buffer_write(&tcp_data->recv, payload, payload_len, BUFFER_BLOCKING);
/*send an ACK of segment put into buffer, piggybacked or not*/
if (circular_buffer_count(&tcp_data->send)) {
process_send_buffer(si, entry, ESTABLISHED);
} else {
send_ack(si, entry, tcp_data);
}
return in_packet;
}
int send_ack(serverinfo_t *si, chisocketentry_t *entry, tcp_data_t *tcp_data)
{
/* send ACK in response to ACK that acks something not yet sent */
tcp_packet_t *out_packet;
tcphdr_t *out_header;
chilog(INFO, "Sending generic ACK");
out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
out_header = TCP_PACKET_HEADER(out_packet);
out_header->seq = htonl(tcp_data->SND_NXT);
out_header->win = htons(get_advertised_window(tcp_data));
out_header->ack_seq = htonl(tcp_data->RCV_NXT);
out_header->ack = 1;
out_header->fin = 0;
chilog(INFO,"sending packet:");
chilog_tcp(INFO,out_packet,LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
/*return and drop the segment*/
chitcp_tcp_packet_free(out_packet);
return CHITCP_OK;
}
int send_fin(serverinfo_t *si, chisocketentry_t *entry, tcp_data_t *tcp_data)
{
tcp_packet_t *out_packet = malloc(sizeof(tcp_packet_t));
chitcpd_tcp_packet_create(entry, out_packet, NULL, 0);
tcphdr_t *out_header = TCP_PACKET_HEADER(out_packet);
out_header->seq = htonl(tcp_data->SND_NXT);
out_header->win = htons(get_advertised_window(tcp_data));
out_header->ack_seq = htonl(tcp_data->RCV_NXT);
out_header->ack = 0;
out_header->fin = 1;
/* Bump SND.NXT when we send a fin. */
tcp_data->SND_NXT++;
chilog(INFO, "sending FIN packet:");
chilog_tcp(INFO, out_packet, LOG_OUTBOUND);
chitcpd_send_tcp_packet(si, entry, out_packet);
chitcp_tcp_packet_free(out_packet);
return CHITCP_OK;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment