Beispiel #1
0
/* ***************************************************
 * Function: handle_timeout
 * ***************************************************
 * This function is called whenever there is a timeout. Unless we
 * are in the TIME_WAIT state, I retransmit all the packets starting
 * from the beginning of the window.  
 * The RTO is also doubled when there is a retransmission as described
 * in K&R. 
 */
static void handle_timeout(mysocket_t sd, context_t *ctx)
{	
	if (ctx->connection_state==CSTATE_TIME_WAIT){
		ctx->connection_state = CSTATE_CLOSED;
		ctx->done = true;
		return;
	}
	our_dprintf("TIMEOUT waiting for seq:%d\n", ctx->seq_base);	
	assert(ctx->sbuffer.num_entries>0 || !DEBUG);
	assert(ctx->sbuffer.start->next!=NULL || !DEBUG);
	struct p_entry *curr = ctx->sbuffer.start->next;
	ctx->rto= MIN(ctx->rto*2, MAX_RTO);
	/* Retransmit all packets */
	while (curr){
			struct tcphdr *hdr = (struct tcphdr*)curr->packet;
			if (curr->num_transmits>=MAX_RT_TRIES){
				our_dprintf("MAX NUMBER OF TRIES REACHED KILLING CONNECTION !!!!\n\n");
				ctx->done = true;
				errno = ETIMEDOUT;
				return;
			}
			hdr->th_ack = ctx->last_ack_sent;
			curr->num_transmits+=1; /* increase counter */
			gettimeofday(&(curr->tstart),NULL);
			network_send(sd, hdr, curr->packet_size);
			curr = curr->next;
			our_dprintf("Retransmitting Seq:%d with Len:%d New RTO:%f\n",
				 hdr->th_seq, ctx->sbuffer.start->next->packet_size, ctx->rto);
	}
}
Beispiel #2
0
/* ***************************************************
 * Function: passive_init
 * ***************************************************
 * Passive node waits for a SYN packet to arrive, it then
 * sends an SYN_ACK using another helper function (below)
 */
static bool passive_init(mysocket_t sd, context_t *ctx)
{
	ctx->connection_state = CSTATE_LISTEN;
	struct tcphdr* syn_packet = (struct tcphdr *)calloc(1,
				(TH_MAX_OFFSET*sizeof(uint32_t))+STCP_MSS);
   /* Listen for a connection request */
	while (true){
		stcp_wait_for_event(sd, NETWORK_DATA, NULL);
 		network_recv(sd, syn_packet);
		if (syn_packet->th_flags == TH_SYN){
			break;	
		}
	}
	our_dprintf("PASSIVE - RCVD SYN with seq: %d\n", syn_packet->th_seq);
	
	/* Prepare SYN_ACK*/			
	syn_packet->th_ack = syn_packet->th_seq + 1;
	syn_packet->th_seq = ctx->initial_sequence_num;
	syn_packet->th_off = TH_MIN_OFFSET;
	syn_packet->th_flags = TH_SYN | TH_ACK;
	syn_packet->th_win = RWIN;
	/*Update connection state variables */	
	ctx->last_ack_sent = syn_packet->th_ack;
	ctx->nxt_seq_num = ctx->initial_sequence_num + 1;
	ctx->connection_state = CSTATE_SYN_RCVD;
	ctx->seq_base = ctx->nxt_seq_num; 
	our_dprintf("PASSIVE - SEND SYN_ACK Packet with seq: %d and ack: %d \n",
				 syn_packet->th_seq, syn_packet->th_ack);
	bool success = send_syn_ack(sd, ctx, syn_packet);

	free(syn_packet);
	return success;
}
Beispiel #3
0
/* ***************************************************
 * Function: handle_ack
 * ***************************************************
 * We received an ACK.  Check the special case that we received
 * a duplicate SYN_ACK and if so, send an ACK back (this is the only 
 * ACK packet that requires an ACK to be sent back). Then check if the
 * ACK falls within the sender window and if so, estimate the new rto
 * (using helper function below), remove everything upto that sequence
 * number from the window. Finally, check to see if this is a FIN ACK
 * and update state variables.
 */
static void handle_ack(struct tcphdr* hdr,  mysocket_t sd, context_t* ctx)
{
	our_dprintf("Ack:%d received - Prior Window Bottom:%d - Window Top: %d\n",
				 hdr->th_ack, ctx->seq_base, ctx->nxt_seq_num);	
	if (hdr->th_flags & TH_SYN) {
		/* Received a duplicate SYN_ACK during init - can only happen first time around for client */
		assert(((hdr->th_seq == ctx->last_ack_sent-1) && 
			(hdr->th_ack==ctx->initial_sequence_num+1)) || !DEBUG);
		if((hdr->th_seq == ctx->last_ack_sent-1) && 
			(hdr->th_ack==ctx->initial_sequence_num+1)){
			send_ack(sd,ctx); 
			return;
		}
	}
	if (!((hdr->th_ack <= ctx->nxt_seq_num) && (hdr->th_ack > ctx->seq_base))) {
		our_dprintf("Ack outside window. Throw Away!\n");
		return;	
	}
	/* ACK INSIDE WINDOW - HANDLE IT */
	estimate_rto(ctx, hdr->th_ack); /* check if we can get a new RTO*/
	ctx->seq_base = hdr->th_ack; /* new window bottom */
	remove_until_seq(ctx); /* remove everything upto the new window bottom (cumulative acks)*/
	
	/* Check to see if we got ACK for FIN.  If so update state variables*/
	if (hdr->th_ack == ctx->fin_ack) {
   		if (ctx->connection_state == CSTATE_FIN_WAIT1) {
   			ctx->connection_state = CSTATE_FIN_WAIT2;				
   		}
   		else if (ctx->connection_state == CSTATE_LAST_ACK) {
   			assert((ctx->nxt_seq_num==ctx->seq_base) || !DEBUG);
   			ctx->connection_state = CSTATE_CLOSED;
   			ctx->done = 1;   
   		}
	}
}
Beispiel #4
0
/* ***************************************************
 * Function: active_init
 * ***************************************************
 * 	Initiate the active side connection.  Send the SYN packet,
 * wait for the SYN_ACK and then send the final SYN. 
 */
static bool active_init(mysocket_t sd, context_t *ctx)
{
	
	struct tcphdr* syn_packet = (struct tcphdr *)calloc(1,(TH_MAX_OFFSET*sizeof(uint32_t)) + STCP_MSS);	
	if (!sendrcv_syn(sd, ctx, syn_packet)) {
		return false;	/* Timeout waiting for SYN_ACK*/
	}
	
	ctx->connection_state = CSTATE_SYN_SENT;
	our_dprintf("Active - RVCD SYN_ACK Seq: %d, ACK: %d \n", syn_packet->th_seq, syn_packet->th_ack);
	ctx->recv_win = MIN(syn_packet->th_win, CWIN);

	/* Send final ACK ---- Prepare Headers */
	syn_packet->th_ack = syn_packet->th_seq + 1; 
	syn_packet->th_seq = ctx->nxt_seq_num;		 
	syn_packet->th_flags = TH_ACK;
	syn_packet->th_off = TH_MIN_OFFSET;
	syn_packet->th_win = RWIN;
	our_dprintf("Active - Sending Final ACK Seq: %d, ACK: %d \n", syn_packet->th_seq, syn_packet->th_ack);
	
	network_send(sd, syn_packet, (TH_MIN_OFFSET*sizeof(uint32_t)));
	
	/* Update connection state variables */
	ctx->seq_base = ctx->nxt_seq_num;
	ctx->last_ack_sent = syn_packet->th_ack;
	free(syn_packet);
	return true;
}
Beispiel #5
0
/* ***************************************************
 * Function: send_syn_ack
 * ***************************************************
 * Passive node calls this to send SYN_ACK and wait for
 * the final ACK packet. It tries a total of 6 times before
 * giving up.
 */
static bool send_syn_ack(mysocket_t sd, context_t *ctx, struct tcphdr* syn_packet)
{
	struct tcphdr* ack_packet = (struct tcphdr *)calloc(1,(TH_MAX_OFFSET*sizeof(uint32_t))+STCP_MSS);
	unsigned int event;
	bool success = false;
	int num_tries = 1;
	struct timeval tstart;
	gettimeofday(&tstart, NULL); /*Start the timer */
	network_send(sd, syn_packet, (TH_MIN_OFFSET*sizeof(uint32_t)));
	while((num_tries < MAX_RT_TRIES) && !success){
		event = wait_for_event(sd, NETWORK_DATA, ctx, tstart);
		if (event & NETWORK_DATA) {
			network_recv(sd, ack_packet);
			our_dprintf("Passive (ACK waiting): Ack: %d, Seq:%d, Next Seq:%d, Last Ack Sent: %d\n", 
						 ack_packet->th_ack, ack_packet->th_seq, ctx->nxt_seq_num, ctx->last_ack_sent);
			success =  ((ack_packet->th_flags == TH_ACK) &&
						(ack_packet->th_ack == ctx->nxt_seq_num));
		}
		else if (event==TIMEOUT){
			our_dprintf("Passive Final INIT ACK not Received TIMEOUT!\n");	
			num_tries+=1;
			gettimeofday(&tstart, NULL);
			our_dprintf("PASSIVE - SEND SYN_ACK Packet with seq: %d and ack: %d \n",
						 syn_packet->th_seq, syn_packet->th_ack);
			network_send(sd, syn_packet, (TH_MIN_OFFSET*sizeof(uint32_t)));
		}
	}
	if (success){
		our_dprintf("PASSIVE - RCVD Last Ack Packet has seq: %d and ack: %d \n",
					 ack_packet->th_seq, ack_packet->th_ack);
   		ctx->recv_win = MIN(syn_packet->th_win, CWIN);
	}	
	free(ack_packet);
	return success;
}
Beispiel #6
0
/* ***************************************************
 * Function: handle_app_data
 * ***************************************************
 * The application has new data to be sent out.  First make sure that
 * there is some space in the window.  Get maximum amount of data from 
 * the application, added it to the sender's buffer, and send it across
 * the network.
 */
static void handle_app_data(mysocket_t sd, context_t *ctx)
{
   size_t bytes_in_transit = ctx->nxt_seq_num - ctx->seq_base;
   size_t win_space = ctx->recv_win - bytes_in_transit;
   if (win_space>0){
       char *pkt = (char *)calloc(1,(TH_MIN_OFFSET*sizeof(uint32_t)) + STCP_MSS);
       struct tcphdr* hdr = (struct tcphdr*)pkt;
       hdr->th_off = TH_MIN_OFFSET;
       hdr->th_seq = ctx->nxt_seq_num;
       hdr->th_flags = 0;
       hdr->th_win = RWIN;
       /* Pick the minimum between space in the window and MSS */
       size_t data_max = MIN(STCP_MSS, win_space);	           
       size_t len = stcp_app_recv(sd, pkt + TCP_DATA_START(hdr), data_max); 	           
       ctx->nxt_seq_num += len; /*add len to obtain nxt_seq_num to be used */
       add_to_send_buffer(ctx,  pkt, (TH_MIN_OFFSET*sizeof(uint32_t)) + len);
 	   our_dprintf("Sending packet with seq: %d of size: %d\n", 
 	   				hdr->th_seq,(TH_MIN_OFFSET*sizeof(uint32_t)) + len);
  	   network_send(sd, pkt, (TH_MIN_OFFSET*sizeof(uint32_t)) + len);
       free(pkt);
   }
   else {
   		our_dprintf(".");           	
   }	
}
Beispiel #7
0
int handle_cstate_fin_wait_2(mysocket_t sd, context_t * ctx){
    int ret = 0;
    unsigned int event;

    our_dprintf("in CSTATE_FIN_WAIT_2\n");
    event = stcp_wait_for_event(sd, NETWORK_DATA | TIMEOUT, NULL);

    /* check whether it was the network, app, or a close request */


    uint8_t buff[MAX_WINDOW_SIZE + sizeof(struct tcphdr)];
    if (event & NETWORK_DATA) {
        /* there was network data received */

        size_t recd;
        recd = stcp_network_recv(sd, buff, MAX_WINDOW_SIZE);
        if (recd == 0) {
            our_dprintf("bad network recv\n");
            return -1;
        }

        struct tcphdr * tcp_packet = (struct tcphdr *) buff;

        if (tcp_packet->th_flags & TH_FIN){
            /*  if fin */
            ctx->recd_last_byte_recd++;
            ctx->recd_last_byte_read++;
            ctx->recd_next_byte_expected++;
            send_syn_ack_fin(sd, ctx, SEND_ACK, 0, ctx->recd_next_byte_expected);
            ctx->connection_state = CSTATE_TIME_WAIT;
        }

    } 
    return ret;    
}
Beispiel #8
0
/* ***************************************************
 * Function: control_wait
 * ***************************************************
 * This function determines the right event to wait for based on window size
 * and connection state.
 * If the window is full, then we don't wait for application data as we know
 * we can't send any.
 * If there are bytes outstanding (not-acked), then we need to wait using a timeout
 * value.
 * If we are TIME_WAIT state, then we set the RTO to the TIME_WAIT_VALUE and only accept
 * network data (i.e. FIN that need to be acknowledged).
 */
unsigned int control_wait(mysocket_t sd, context_t *ctx, bool fin_retry)
{
	unsigned int event = 0;
	size_t bytes_in_transit = ctx->nxt_seq_num - ctx->seq_base;
  	size_t win_space = ctx->recv_win - bytes_in_transit;
  		
	unsigned int wait_flags = NETWORK_DATA | APP_CLOSE_REQUESTED;
	if (win_space >0 && !fin_retry) {
		wait_flags |= APP_DATA;	
	}
	/* Is there something in flight? */
	if (bytes_in_transit > 0) {
		assert((ctx->sbuffer.num_entries>0) || !DEBUG);
		our_dprintf("Waiting with RTO for Seq:%d \n", ctx->seq_base);
		event = wait_with_rto(sd, wait_flags, ctx); 
	}
	else if (ctx->connection_state==CSTATE_TIME_WAIT) {
		ctx->rto = TIME_WAIT_VALUE;
		struct timeval current_time;
		gettimeofday(&current_time, NULL);
		our_dprintf("IN TIME WAIT!!!\n");
		event = wait_for_event(sd, NETWORK_DATA, ctx,current_time);    			
	}
	else {
		/* No unacked packets... take your sweet time*/
		event = stcp_wait_for_event(sd, ANY_EVENT, NULL);
	}
	return event;	
}
Beispiel #9
0
int handle_cstate_est_send(mysocket_t sd, context_t * ctx){
    //our_dprintf("*IN EST SEND\n");
    size_t eff_wind = calc_eff_window(ctx);
    size_t max_to_send = MIN(eff_wind, STCP_MSS);
    if (max_to_send == 0) {
        //our_dprintf("window too small to send min(%u, %u) \n", eff_wind, STCP_MSS);
        return 0;
    }
    //our_dprintf("max to send %u\n", max_to_send);
    /* receive data from app */
    size_t recd_app;
    uint8_t buff[max_to_send];
    recd_app = stcp_app_recv(sd, buff, max_to_send);

    /* construct header */
    uint32_t header_size = sizeof(struct tcphdr);
    uint8_t header_buf[header_size];
    memset(header_buf, 0, header_size);

    struct tcphdr * tcp_header = ( struct tcphdr *) header_buf;

    tcp_header->th_seq = htonl(ctx->sent_last_byte_sent + 1);
    tcp_header->th_flags |= TH_SYN;
    

    tcp_header->th_off = 5; /* no options, data begins 20 bytes into packet */

    /* set adv window size */
    tcp_header->th_win = htons(calc_adv_wind(ctx));
    ctx->sent_adv_window = calc_adv_wind(ctx);
    /* copy the data into the tcp buffer */

    uint32_t wind_index = ((ctx->sent_last_byte_written + 1) 
                - ctx->initial_sequence_num) % MAX_WINDOW_SIZE;
    our_dprintf("window index %u\n", wind_index);
    /* we're wrapping the buffer */
    if (wind_index + recd_app > MAX_WINDOW_SIZE) {
        size_t delta = MAX_WINDOW_SIZE - wind_index;
        our_dprintf("wrapping the buffer\n");
        /* fill the end of the buffer */
        memcpy(ctx->send_wind + wind_index, buff, delta);
        /* restart at the beginning of the buffer */
        memcpy(ctx->send_wind, buff+delta, recd_app - delta);

        stcp_network_send(sd, header_buf, header_size, ctx->send_wind + wind_index, delta, 
            ctx->send_wind, recd_app - delta, NULL);
    } else {
        /* don't need to wrap the buffer */
        our_dprintf("not wrapping the buffer\n");
        memcpy(ctx->send_wind + wind_index, buff, recd_app);
        stcp_network_send(sd, header_buf, header_size, (ctx->send_wind + wind_index), recd_app, NULL);
    }
    ctx->sent_last_byte_sent += recd_app;

    ctx->sent_last_byte_written += recd_app;
    return 0;

}
Beispiel #10
0
int handle_cstate_fin_wait_1(mysocket_t sd, context_t * ctx){
    int ret = 0;
    unsigned int event;

    our_dprintf("in CSTATE_FIN_WAIT_1\n");
    event = stcp_wait_for_event(sd, NETWORK_DATA | TIMEOUT, NULL);

    /* check whether it was the network, app, or a close request */

    uint8_t buff[MAX_WINDOW_SIZE + sizeof(struct tcphdr)];
    if (event & NETWORK_DATA) {
        /* there was network data received */

        size_t recd;
        recd = stcp_network_recv(sd, buff, MAX_WINDOW_SIZE);
        if (recd == 0) {
            our_dprintf("bad network recv\n");
            return -1;
        }

        struct tcphdr * tcp_packet  = (struct tcphdr *) buff;

        if (tcp_packet->th_flags & (TH_ACK | TH_FIN)) {
            /* if ack + fin */
            if (ntohl(tcp_packet->th_ack) < ctx->sent_last_byte_acked || ntohl(tcp_packet->th_ack) > ctx->sent_last_byte_written+1){
                our_dprintf("bad ack, expected %u, received %u. returning \n", ctx->sent_last_byte_sent+1, tcp_packet->th_ack);
                return -1;
            }
            ctx->sent_last_byte_acked = ntohl(tcp_packet->th_ack);
            ctx->recd_last_byte_recd++;
            ctx->recd_last_byte_read++;
            ctx->recd_next_byte_expected++;
            send_syn_ack_fin(sd, ctx, SEND_ACK, 0, ctx->recd_next_byte_expected);
            ctx->connection_state = CSTATE_TIME_WAIT;
        } else if (tcp_packet->th_flags & TH_ACK){
            /* ack received */
            if (ntohl(tcp_packet->th_ack) < ctx->sent_last_byte_acked || ntohl(tcp_packet->th_ack) > ctx->sent_last_byte_written+1){
                
                our_dprintf("bad ack, expected %u, received %u. returning \n", ctx->sent_last_byte_sent+1, tcp_packet->th_ack);
                return -1;
            }
            ctx->sent_last_byte_acked = ntohl(tcp_packet->th_ack);
            //our_dprintf("*** got an ack %u\n", ctx->sent_last_byte_acked);
            ctx->connection_state = CSTATE_FIN_WAIT_2;

        } else if (tcp_packet->th_flags &  TH_FIN){
        /* else if fin */
            ctx->recd_last_byte_recd++;
            ctx->recd_last_byte_read++;
            ctx->recd_next_byte_expected++;
            send_syn_ack_fin(sd, ctx, SEND_ACK, 0, ctx->recd_next_byte_expected);
            ctx->connection_state = CSTATE_CLOSING;
        }

    } 

    return ret;    
}
Beispiel #11
0
int handle_cstate_close_wait(mysocket_t sd, context_t * ctx){
    int ret = 0;
    unsigned int event;

    our_dprintf("in CSTATE_CLOSE_WAIT\n");
    struct timespec wait_time;
    time_t curtime;

    curtime = time(0);
    //our_dprintf("curtime = %u\n", curtime);
    wait_time.tv_sec = curtime + 240; /* wait atleast 2 lifetimes */
    wait_time.tv_nsec = 0; 
    event = stcp_wait_for_event(sd, APP_CLOSE_REQUESTED | TIMEOUT, &wait_time);

    /* check whether it was the network, app, or a close request */

    if (event & APP_CLOSE_REQUESTED) {
        /* there was network data received */

        send_syn_ack_fin(sd, ctx, SEND_FIN, 0, 0);
        ctx->connection_state = CSTATE_LAST_ACK;

    } 

    return ret;    
}
Beispiel #12
0
int open_tcp_conn(mysocket_t sd, context_t * ctx, bool_t is_active) {

    /* make sure the state is closed before we try to open it */
    assert(ctx->connection_state == CSTATE_CLOSED);
    while (ctx->connection_state != CSTATE_ESTABLISHED && 
        ctx->connection_state != CSTATE_FIN_WAIT_1 ) {
        switch(ctx->connection_state) {
            case CSTATE_CLOSED:
                /* connection is closed */
                handle_cstate_closed(sd, ctx, is_active);
                break;
            case CSTATE_LISTEN:
                /* listening */
                handle_cstate_listen(sd, ctx);
                break;
            case CSTATE_SYN_RCVD:
                /* syn has been received */
                handle_cstate_syn_rcvd(sd, ctx);
                break;
            case CSTATE_SYN_SENT:
                /* syn has been sent */
                handle_cstate_syn_sent(sd, ctx);
                break;
            default:
                /* shouldn't happen */
                our_dprintf("bad open tcp conn state\n");
                assert(0);
                break;
        }
    }
    return 0;
}
Beispiel #13
0
/* ***************************************************
 * Function: transport_init
 * ***************************************************
 * initialise the transport layer, and start the main loop, handling
 * any data from the peer or the application.  this function should not
 * return until the connection is closed.
 */
void transport_init(mysocket_t sd, bool_t is_active)
{
    context_t *ctx = init_ctx(is_active);
	bool success = false;
    if (is_active) {
		success = active_init(sd, ctx);		
    }
    else {
    	success = passive_init(sd, ctx);    	
    }
	if (success){  	
  	    ctx->connection_state = CSTATE_ESTABLISHED;
	    stcp_unblock_application(sd);
	    control_loop(sd, ctx);
	}
	else {
		errno = ETIMEDOUT;
		stcp_unblock_application(sd);
	}
	teardown_resources(ctx);
	/* Wait for potential pending processes in 
	 * other side (this is to fix a bug in the starter code) */
	sleep(1);		
	our_dprintf("Exiting Loop!\n");
}
Beispiel #14
0
/* ***************************************************
 * Function: control_loop
 * ***************************************************
 * control_loop() is the main STCP loop; it repeatedly waits for one of the
 * following to happen:
 *   - incoming data from the peer
 *   - new data from the application (via mywrite())
 *   - the socket to be closed (via myclose())
 *   - a timeout
 */
static void control_loop(mysocket_t sd, context_t *ctx)
{
    assert(ctx);
	unsigned int event = 0;
	bool fin_retry = false; /* TRUE = APP_CLOSE_REQUESTED but window was full */
    while (!ctx->done)
    {
    	size_t bytes_in_transit = ctx->nxt_seq_num - ctx->seq_base;
  		size_t win_space = ctx->recv_win - bytes_in_transit;
    	event = control_wait(sd, ctx, fin_retry);
		if (event & NETWORK_DATA) {
		   handle_network_data(sd, ctx);
		}
		if ((event & APP_DATA) && !fin_retry) {
			handle_app_data(sd, ctx); 
		}
		if (event == TIMEOUT){
			handle_timeout(sd, ctx);

		}
		if ((event & APP_CLOSE_REQUESTED) || fin_retry) {
   			if (win_space>0){
				send_fin(sd, ctx);
				fin_retry = false;
   			}
   			else {
   				our_dprintf("App requested FIN but window is full\n");
   				fin_retry = true;	
   			}
		 }
    }
}
Beispiel #15
0
/* ***************************************************
 * Function: handle_fin
 * ***************************************************
 * This function is called when we receive a FIN (or actually
 * when we process a FIN in-order from the receive buffer).  It
 * simply updates the states variables and calls stcp_fin_received.
 * 
 */
static void handle_fin(mysocket_t sd, context_t *ctx)
{
   	size_t bytes_in_transit = ctx->nxt_seq_num - ctx->seq_base;
   	our_dprintf("FIN PACKET RECEIVED. ACK SEN. Window Start:%d Window End:%d\n",
   				 ctx->seq_base, ctx->nxt_seq_num);
    
    /* This is a special case, we sent a FIN first but we received another 
     * FIN before we got an ACK for the first FIN.  If this is the case, I just
     * treat that second FIN as an ACK for the first FIN (though this would not be
     * necessarily true if both sides were to send FINs simulatenously). We were
     * not asked to deal with simulatenous shut-down so I just do it this clean
     * way */
    if (ctx->connection_state == CSTATE_FIN_WAIT1) {
    	assert(((ctx->seq_base + 1) == ctx->nxt_seq_num) || !DEBUG);
    	ctx->seq_base = ctx->nxt_seq_num;
    	remove_until_seq(ctx);
    	ctx->connection_state = CSTATE_TIME_WAIT;
    }	
    else if (ctx->connection_state == CSTATE_FIN_WAIT2) {
		assert(bytes_in_transit==0 || !DEBUG);
		ctx->connection_state = CSTATE_TIME_WAIT;
	}
	else  {
		assert(ctx->connection_state = CSTATE_ESTABLISHED || !DEBUG);
		assert(bytes_in_transit==0 || !DEBUG);
		ctx->connection_state = CSTATE_CLOSE_WAIT;
	}
	stcp_fin_received(sd);
}
Beispiel #16
0
int handle_cstate_listen(mysocket_t sd, context_t * ctx) {
    int ret = 0;
    unsigned int event;

    our_dprintf("in CSTATE_LISTEN\n");
    
    event = stcp_wait_for_event(sd, ANY_EVENT, NULL);

    /* check whether it was the network, app, or a close request */

    if (event & NETWORK_DATA) {
        /* there was network data received */

        size_t recd;
        recd = stcp_network_recv(sd, ctx->recv_wind, MAX_WINDOW_SIZE);
        if (recd == 0) {
            our_dprintf("bad network recv\n");
            return -1;
        }
        struct tcphdr * tcp_packet  = (struct tcphdr *) ctx->recv_wind;
        //size_t recd_app = recd - 4 * tcp_packet->th_off;
        if (tcp_packet->th_flags & TH_SYN){
            /* syn received */
            ctx->initial_recd_seq_num = ntohl(tcp_packet->th_seq); 
            ctx->recd_last_byte_recd = ctx->initial_recd_seq_num;
            ctx->recd_next_byte_expected = ctx->recd_last_byte_recd + 1;
            ctx->recd_adv_window = ntohs(tcp_packet->th_win);
            send_syn_ack_fin(sd, ctx, SEND_SYN | SEND_ACK, 
                ctx->initial_sequence_num, ctx->recd_next_byte_expected);
            ctx->connection_state = CSTATE_SYN_RCVD;
        }
    } 
    if (event & APP_DATA) {
        /* the application has requested that data be sent */
        send_syn_ack_fin(sd, ctx, SEND_SYN, ctx->initial_sequence_num, 0);
        ctx->connection_state = CSTATE_SYN_SENT;
    } 

    if (event & APP_CLOSE_REQUESTED) {
        /* the application has requested that the conn be closed */
        ctx->connection_state = CSTATE_CLOSED;
    } 
    return ret;
}
Beispiel #17
0
int handle_cstate_syn_rcvd(mysocket_t sd, context_t * ctx) {

    int ret = 0;
    unsigned int event;

    our_dprintf("in CSTATE_SYN_RCVD\n");
    event = stcp_wait_for_event(sd, NETWORK_DATA | APP_CLOSE_REQUESTED | TIMEOUT, NULL);

    /* check whether it was the network, app, or a close request */
    if (event & NETWORK_DATA) {
        /* there was network data received */

        size_t recd;
        recd = stcp_network_recv(sd, ctx->recv_wind, MAX_WINDOW_SIZE);
        if (recd == 0) {
            our_dprintf("bad network recv\n");
            return -1;
        }

        struct tcphdr * tcp_packet  = (struct tcphdr *) ctx->recv_wind;

        if (tcp_packet->th_flags & TH_ACK){
            /* ack received */
            if (ntohl(tcp_packet->th_ack) < ctx->sent_last_byte_acked || ntohl(tcp_packet->th_ack) > ctx->sent_last_byte_written+1){
                our_dprintf("bad ack, expected %u, received %u. returning \n", ctx->sent_last_byte_sent+1, tcp_packet->th_ack);
                return -1;
            }
            ctx->sent_last_byte_acked = ntohl(tcp_packet->th_ack);
            ctx->recd_adv_window = ntohs(tcp_packet->th_win);
            //our_dprintf("*** got ack %u, got adv window %u\n", ctx->sent_last_byte_acked, ctx->recd_adv_window);
            ctx->connection_state = CSTATE_ESTABLISHED;
        }
    } 

    if (event & APP_CLOSE_REQUESTED) {
        /* the application has requested that the conn be closed */

        ctx->connection_state = CSTATE_FIN_WAIT_1;
        close_tcp_conn(sd, ctx);
    } 
    return ret;
}
Beispiel #18
0
/* ***************************************************
 * Function: add_to_recv_buffer
 * ***************************************************
 *  This function simply adds a packet (if its a valid packet, defined
 *  by the next function) to the receiver buffer.  It searches the receiver
 *  buffer for the right place to put the file.  The receive buffer is always
 *  kept in anscending order of sequence numbers.   
 */
static void add_to_recv_buffer(context_t *ctx, char* pkt, size_t len)
{	
	tcp_seq target_seq = ((struct tcphdr *)pkt)->th_seq;
	size_t data_len = len - TCP_DATA_START(pkt);	
	if (!is_valid_entry(ctx,target_seq, data_len)){
		our_dprintf("Invalid incoming packet - dropping!\n");
		return;	
	}
	struct window* win = &(ctx->rbuffer);
	assert((win->end->next==NULL) || !DEBUG);
	/* Create a new link list entry */
	struct p_entry* new_entry = (struct p_entry*)calloc(1,sizeof(struct p_entry));
	new_entry->packet = (char *)calloc(1, len);
	memcpy(new_entry->packet, pkt, len);
	new_entry->packet_size = len;
	tcp_seq curr_seq;
	struct p_entry* prev = win->start;
	struct p_entry* curr = win->start->next;
	bool inserted = false;
	/* Search for the right place to add this sequence */
	while(curr) {
		curr_seq = ((struct tcphdr *)curr->packet)->th_seq;
		if (target_seq < curr_seq) {
			/* Place found */
			inserted = true;
			new_entry->next = curr;
			prev->next = new_entry;
			break;
		}
		prev = curr;
		curr = curr->next;
	}
	/* Add to the end of the list if not added before */
	if (!inserted){
		new_entry->next = NULL;	
		win->end->next = new_entry;
		win->end = win->end->next;
	}
	win->num_entries+=1;
	our_dprintf("Added Recv: %d to receive buffer\n", ((struct tcphdr*)pkt)->th_seq);
}
Beispiel #19
0
/* ***************************************************
 * Function: is_valid_entry
 * ***************************************************
 *  Determines if an incoming packet "fits" in the receive buffer. 
 *  The following conditions must be sastified.  
 * 	1.) The incoming sequence number better be greater than or equal to
 * 	    the last ack we sent out, otherwise we already have the data.
 *  2.) The incoming sequence number cannot be identical to any sequence
 * 		number in the receive buffer.  This just means we have a duplicate
 * 		packet so no need to store it.
 *  3.) If the packet fits between sequence X and sequence Y, its length
 * 		better not be large enough to overlap with Y, and the length of X
 * 		better not be large enough to overlap with the packet's sequence.
 * 		This means the packet is trying to overwrite data and I simply drop it.
 */
static bool is_valid_entry(context_t* ctx, tcp_seq target_seq, size_t data_len)
{
	if (target_seq< ctx->last_ack_sent){
		our_dprintf("Received Packet Outside receiver window with seq: %d\n", target_seq);
		return false;	
	}
	struct p_entry* prev = ctx->rbuffer.start;
	struct p_entry* curr = ctx->rbuffer.start->next;
	while(curr) {
		tcp_seq curr_seq = ((struct tcphdr *)curr->packet)->th_seq;
		if (target_seq==curr_seq){
			our_dprintf("Received Duplicate Packet with seq: %d\n", target_seq);
			return false;
		}
		else if (target_seq < curr_seq) {
			if (curr->next){
				tcp_seq next_seq = ((struct tcphdr *)curr->next->packet)->th_seq;	
				if ( (target_seq + data_len) > next_seq) {
					our_dprintf("Packet overwriting 1!!! TARGET SEQ:%d DATA_LEN:%d NEXT_SEQ:%d CURR SEQ:%d\n",
								 target_seq, data_len, curr_seq);
					assert(!DEBUG);
					return false;
				}	
			}
			if (prev!=ctx->rbuffer.start) {
				tcp_seq prev_seq = ((struct tcphdr *)prev->packet)->th_seq;
				size_t prev_len = prev->packet_size - TCP_DATA_START(prev->packet);
				if ((target_seq>prev_seq) && ((prev_seq + prev_len) > target_seq)){
					our_dprintf("Packet overwriting 2!!! PREV SEQ:%d PREV_LEN:%d TARGET_SEQ:%d CURR SEQ:%d\n",
								 prev_seq, prev_len, target_seq, curr_seq);
					assert(!DEBUG);
					return false;
				}		
				
			}
		}
		prev = curr;
		curr = curr->next;
	}
	return true;
}
Beispiel #20
0
int handle_cstate_closed(mysocket_t sd, context_t * ctx, bool_t is_active) {

    int ret = 0;
    our_dprintf("in CSTATE_CLOSED\n");
    if (is_active) {
        send_syn_ack_fin(sd, ctx, SEND_SYN, ctx->initial_sequence_num, 0);
        ctx->connection_state = CSTATE_SYN_SENT;
    } else {
        ctx->connection_state = CSTATE_LISTEN;
    }
    return ret;
}
Beispiel #21
0
/* ***************************************************
 * Function: send_ack
 * ***************************************************
 * Small helper function for preparing an ACK packet and sending
 * it to the network.   
 */
static void send_ack(mysocket_t sd, context_t* ctx)
{
	struct tcphdr *ack_pkt = (struct tcphdr *)calloc(1, (TH_MIN_OFFSET*sizeof(uint32_t)));
	ack_pkt->th_flags = TH_ACK;
	ack_pkt->th_ack = ctx->last_ack_sent;
	ack_pkt->th_seq = ctx->nxt_seq_num;
	our_dprintf("Sending ack: %d with seq: %d\n", ack_pkt->th_ack, ack_pkt->th_seq);	
	ack_pkt->th_win = RWIN;
	ack_pkt->th_off = TH_MIN_OFFSET;
	network_send(sd, ack_pkt, TH_MIN_OFFSET*sizeof(uint32_t));
	free(ack_pkt);	
}
Beispiel #22
0
int close_tcp_conn(mysocket_t sd, context_t * ctx) {

    while(ctx->connection_state != CSTATE_CLOSED) {
        switch (ctx->connection_state) {

            case CSTATE_FIN_WAIT_1:
                /* first fin wait state */
                handle_cstate_fin_wait_1(sd, ctx);
                break;
            case CSTATE_FIN_WAIT_2:
                /* second fin wait state */
                handle_cstate_fin_wait_2(sd, ctx);
                break;
            case CSTATE_CLOSING:
                /* currently closing */
                handle_cstate_closing(sd, ctx);
                break;
            case CSTATE_TIME_WAIT:
                /* waiting timeout */ 
                handle_cstate_time_wait(sd, ctx);
                break;
            case CSTATE_CLOSE_WAIT:
                /* close wait state */
                handle_cstate_close_wait(sd, ctx);
                break;
            case CSTATE_LAST_ACK:
                /* last ack */
                handle_cstate_last_ack(sd, ctx);
                break;
            default:
                /* shouldn't happen */
                our_dprintf("bad close ctcp state\n");
                assert(0);
        }
    }
    ctx->done = TRUE;
    our_dprintf("ALL DONE\n");
    return 0;
}
Beispiel #23
0
/* ***************************************************
 * Function: sendrcv_syn
 * ***************************************************
 * 	Helper function for active-side network initiation.
 *   It sends the SYN packet and waits for the right SYN_ACK
 */
static bool sendrcv_syn(mysocket_t sd, context_t *ctx, struct tcphdr* in_packet)
{
	/* Set SYN Headers */
	struct tcphdr* out_packet = (struct tcphdr *)calloc(1,(TH_MIN_OFFSET*sizeof(uint32_t)));
	out_packet->th_ack = 0; 
	out_packet->th_seq = ctx->initial_sequence_num;
  	out_packet->th_flags = TH_SYN;
	out_packet->th_off = TH_MIN_OFFSET;
	out_packet->th_win = RWIN;
	ctx->nxt_seq_num = ctx->initial_sequence_num + 1; /* SYN Packet =  1 byte */
	our_dprintf("Active Sending SYN.  Seq:%d\n", out_packet->th_seq);
	
	unsigned int event;
	bool success = false;
	int num_tries = 1;
	struct timeval tstart;
	gettimeofday(&tstart,NULL); /*start the timer */
	network_send(sd, out_packet, (TH_MIN_OFFSET*sizeof(uint32_t)));
	/* Try sending the SYN a total of 6 times before giving up */
	while ((num_tries < MAX_RT_TRIES) && !success){
		event = wait_for_event(sd, NETWORK_DATA, ctx, tstart);
		if (event & NETWORK_DATA) {
			network_recv(sd, in_packet);
			/* Check to see if we got the right SYN_ACK */
			success = ((in_packet->th_flags & (TH_SYN | TH_ACK)) &&
			    	  (in_packet->th_ack == ctx->nxt_seq_num));
		}
		else if (event==TIMEOUT){
			our_dprintf("Active - SYN TIMEOUT!, no SYN_ACK!\n");
			gettimeofday(&tstart,NULL);
			num_tries+=1;
			our_dprintf("Active Resending SYN.  Seq:%d\n", out_packet->th_seq);
			network_send(sd, out_packet, (TH_MIN_OFFSET*sizeof(uint32_t)));
		}
	}	
	free(out_packet);
	return success;
}
Beispiel #24
0
int handle_cstate_last_ack(mysocket_t sd, context_t * ctx){
    int ret = 0;

    unsigned int event;
    our_dprintf("in CSTATE_LAST_ACK\n");
    event = stcp_wait_for_event(sd, NETWORK_DATA | TIMEOUT, NULL);

    /* check whether it was the network, app, or a close request */

    uint8_t buff[MAX_WINDOW_SIZE + sizeof(struct tcphdr)];
    if (event & NETWORK_DATA) {
        /* there was network data received */

        size_t recd;
        recd = stcp_network_recv(sd, buff, MAX_WINDOW_SIZE);
        if (recd == 0) {
            our_dprintf("bad network recv\n");
            return -1;
        }

        struct tcphdr * tcp_packet  = (struct tcphdr *) buff;

        if (tcp_packet->th_flags & TH_ACK){
            /*  if ack */
            if (ntohl(tcp_packet->th_ack) < ctx->sent_last_byte_acked || ntohl(tcp_packet->th_ack) > ctx->sent_last_byte_written+1){
                our_dprintf("bad ack, expected %u, received %u. returning \n", ctx->sent_last_byte_sent+1, tcp_packet->th_ack);
                return -1;
            }
            //our_dprintf("*** got an ack %u\n", ctx->sent_last_byte_acked);
            ctx->sent_last_byte_acked = ntohl(tcp_packet->th_ack);
            ctx->connection_state = CSTATE_CLOSED;

        }

    } 
    return ret;    
}
Beispiel #25
0
/* ***************************************************
 * Function: handle_network_data
 * ***************************************************
 *  A packet has arrived.  If its an ACK packet, call a helper 
 * function that deals with acks.  If the packet has data or it's
 * a FIN packet, then add the packet to the receiver buffer only
 * if it fits in the receiving window.  If it doesn't fit,
 * drop the packet.  If it fits, send an acknowledgement.
 */
static void handle_network_data(mysocket_t sd, context_t *ctx)
{
	   char *pkt = (char *)calloc(1, (TH_MAX_OFFSET*sizeof(uint32_t)) + STCP_MSS);
	   size_t total_length = network_recv(sd, pkt);
	   struct tcphdr* hdr = (struct tcphdr *)pkt;
	   ctx->recv_win = MIN(hdr->th_win, CWIN);
	   if (hdr->th_flags & TH_ACK) {
			handle_ack(hdr, sd, ctx);
	   }
	   size_t data_length = total_length - TCP_DATA_START(hdr);
	   if (data_length>0 || (hdr->th_flags & TH_FIN)) {
	   		our_dprintf("Received %d bytes of data with seq: %d, last ack sent: %d\n", 
	   					data_length, hdr->th_seq, ctx->last_ack_sent);
	   		/* Does the packet fit in the receiving window?*/
	   		if ((hdr->th_seq + data_length)<=(ctx->last_ack_sent + RWIN)) {
	   			add_to_recv_buffer(ctx, pkt, total_length);
	   			if (hdr->th_seq == ctx->last_ack_sent){
	   				/* The packet arrived in order. We can remove all in-order
	   				 * packets from the receive buffer now */
	   				our_dprintf("Packet In Order. Handling buffer\n");
	   				handle_recv_buffer(sd, ctx);
	   			}
	   			else {
	   				if (hdr->th_seq > ctx->last_ack_sent) {
	   					our_dprintf("This packet is out of order. Adding to recv buffer\n");
	   				}
	   			}
	   			send_ack(sd, ctx);
	   		}
	   		else {
	   			our_dprintf("Packet outside receiver buffer. Dropping!!!\n");	
	   	
	   		}		
	   }
	   free(pkt);			
}
Beispiel #26
0
/* ***************************************************
 * Function: handle_recv_buffer
 * ***************************************************
 *  This function is called whenever we get the next packet
 *  we are expecting from the sender in-order.  It iterates
 *  through the receive buffer sending packets to the application
 * for consumption.  When a packet is consumed, it is freed and
 * removed from the buffer.  The loop stops as soon as we see
 * that the next packet is not in order or when we have emptied out
 * the buffer entirely (whichever happens first).  
 */
static void handle_recv_buffer(mysocket_t sd, context_t *ctx)
{
	struct window* win = &(ctx->rbuffer);
	struct p_entry* curr = win->start->next;
	assert(curr || !DEBUG);
	tcp_seq next_seq, curr_seq;
	size_t data_len;
	bool is_fin = false;
	bool stop_loop = false;
	/* Iterate through all entries in the receive buffer */
	while (win->num_entries>0 && !stop_loop) {
		data_len = curr->packet_size - TCP_DATA_START(curr->packet);
		curr_seq = ((struct tcphdr *)curr->packet)->th_seq;
		/* Special case, FIN packet in receive buffer */
		is_fin = (((struct tcphdr *)curr->packet)->th_flags & TH_FIN);
		if (is_fin){
			assert(curr->next==NULL || !DEBUG);
			ctx->last_ack_sent+=1;
			if (data_len>0) {
				/* FIN packet with data.  Send data to app */
				stcp_app_send(sd, curr->packet + TCP_DATA_START(curr->packet), data_len);
			}
			handle_fin(sd, ctx); /* Call helper function */
			break;	
		}
		our_dprintf("Sending packet with Seq: %d to application \n", curr_seq);
		ctx->last_ack_sent = curr_seq + data_len;
		stcp_app_send(sd, curr->packet + TCP_DATA_START(curr->packet), data_len);
		/* Have we reached the end of the buffer?*/
		if (curr->next){
			/* No, exit only if next sequence is not in order */
			next_seq = ((struct tcphdr *)curr->next->packet)->th_seq;	
			if ((data_len + curr_seq)!=next_seq){
				stop_loop = true;	
			} 	
		}
		/* Update pointers and free memory */
		win->start->next = curr->next;
		if (win->end == curr){
			win->end = win->start;	
		}
		free(curr->packet);
		free(curr);
		curr = win->start->next;
		win->num_entries-=1;	
	}
}
Beispiel #27
0
/* ***************************************************
 * Function: add_to_send_buffer
 * ***************************************************
 *  A data/fin packet is being sent out. This function adds that packet
 *  to the sender's window (end of link list) in case it timeouts and 
 *  needs to be retransmitted. It records the time the packet was sent.
 */
static void add_to_send_buffer(context_t* ctx, char* pkt, size_t len)
{
	struct window* win = &(ctx->sbuffer);
	assert((win->end->next==NULL) || !DEBUG);
	struct p_entry* new_entry = (struct p_entry*)calloc(1,sizeof(struct p_entry));
	new_entry->packet = (char *)calloc(1, len);
	memcpy(new_entry->packet, pkt, len);
	new_entry->packet_size = len;
	new_entry->next = NULL;
	new_entry->num_transmits = 1;
	gettimeofday(&(new_entry->tstart), NULL);
	win->end->next = new_entry; /* Attach to the end of the list */
	win->end = win->end->next;  /* Update the end of the list */
	win->num_entries+=1;		/* Add 1 to the number of entries */
	assert(win->start->next || !DEBUG);
	our_dprintf("Added Seq: %d to send buffer\n", ((struct tcphdr*)pkt)->th_seq);
} 
Beispiel #28
0
/* initialise the transport layer, and start the main loop, handling
 * any data from the peer or the application.  this function should not
 * return until the connection is closed.
 */
 void transport_init(mysocket_t sd, bool_t is_active)
 {
    context_t *ctx;
    int res;
    ctx = (context_t *) calloc(1, sizeof(context_t));
    assert(ctx);

    generate_initial_seq_num(ctx);
    ctx->connection_state = CSTATE_CLOSED;
    
    ctx->send_wind = (uint8_t *) malloc(MAX_WINDOW_SIZE);
    memset(ctx->send_wind, 0, MAX_WINDOW_SIZE);

    ctx->recv_wind = (uint8_t *) malloc(MAX_WINDOW_SIZE);
    memset(ctx->recv_wind, 0, MAX_WINDOW_SIZE);
    res = open_tcp_conn(sd, ctx, is_active);

    /* XXX: you should send a SYN packet here if is_active, or wait for one
     * to arrive if !is_active.  after the handshake completes, unblock the
     * application with stcp_unblock_application(sd).  you may also use
     * this to communicate an error condition back to the application, e.g.
     * if connection fails; to do so, just set errno appropriately (e.g. to
     * ECONNREFUSED, etc.) before calling the function.
     */

    stcp_unblock_application(sd);


    if (ctx->connection_state == CSTATE_ESTABLISHED){
        control_loop(sd, ctx);
    } else if (ctx->connection_state == CSTATE_FIN_WAIT_1){
        close_tcp_conn(sd, ctx);
    } else if (ctx->connection_state == CSTATE_CLOSED) {
        ;
    }else {
        our_dprintf("bad state in transport init");
        assert(0);
    }

    /* do any cleanup here */
    free(ctx->send_wind);
    free(ctx->recv_wind);
    free(ctx);
}
Beispiel #29
0
/* ***************************************************
 * Function: remove_until_seq
 * ***************************************************
 * When we receive an ACK, this is the function we call to
 * remove all entries up to the new/updated bottom of the 
 * send window.
 */
static void remove_until_seq(context_t* ctx)
{
	struct window* win = &(ctx->sbuffer);
	assert((win->start->next!=NULL) || !DEBUG);
	assert((win->num_entries>0) || !DEBUG);
	while (win->num_entries>0){
		struct p_entry* victim = win->start->next;
		tcp_seq sequence = ((struct tcphdr*)(victim->packet))->th_seq;
		if (sequence>=ctx->seq_base) break;
		our_dprintf("Removing Seq: %d from sender's buffer\n",
					 ((struct tcphdr*)victim->packet)->th_seq);
		if (victim==win->end) {
			win->end = win->start;
		}
		win->start->next = win->start->next->next;
		win->num_entries-=1;
		free(victim->packet);
		free(victim);
	}
}
Beispiel #30
0
/* ***************************************************
 * Function: send_fin
 * ***************************************************
 * The application has requested to be closed and there
 * is at least 1 byte of room in the window. This function
 * prepares a fin packet, adds it to the send buffer, and
 * sends it.  It also updates the connection state.
 */
static void send_fin(mysocket_t sd, context_t *ctx)
{
	our_dprintf("App requested close there is space in window... SENDING FIN\n");
  	struct tcphdr* fin_pkt = (struct tcphdr *)calloc(1,TH_MIN_OFFSET*sizeof(uint32_t));
  	fin_pkt->th_seq = ctx->nxt_seq_num;
  	ctx->fin_ack = fin_pkt->th_seq + 1;
  	ctx->nxt_seq_num += 1; /*FIN = 1 byte of data */
  	fin_pkt->th_off = TH_MIN_OFFSET;
  	fin_pkt->th_flags = TH_FIN;
  	fin_pkt->th_win = RWIN;
  	/* Update State variables depending on previous state */
  	if (ctx->connection_state == CSTATE_ESTABLISHED ) {
  		ctx->connection_state = CSTATE_FIN_WAIT1;
  	}
  	else {
  		assert(ctx->connection_state == CSTATE_CLOSE_WAIT || !DEBUG);
  		ctx->connection_state = CSTATE_LAST_ACK;
  	}
  	add_to_send_buffer(ctx, (char *)fin_pkt, (TH_MIN_OFFSET*sizeof(uint32_t)));
  	network_send(sd, fin_pkt, TH_MIN_OFFSET*sizeof(uint32_t));
	free(fin_pkt);
}