示例#1
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;	
	}
}
示例#2
0
/* 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);
    int tcplen;
    char payload[MAXLEN];
    STCPHeader *in_header;
    void *packtosend;
    char *indicpt;
    char tempbuff[WINLEN];
    stcp_event_type_t eventflag;

    while (!ctx->done)
    {
        unsigned int event;

        eventflag = ANY_EVENT;

        /* set timeout if there is any unack'd data; if not, just make it NULL */
        struct timespec timestart;
        struct timespec *timeout;


        if (ctx->send_unack < ctx->send_next) {
            timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time;
            timeout = &timestart;

            /* constructs the timeout with absolute time by adding the RTO */
            timeout->tv_sec += ctx->rto.tv_sec;
            timeout->tv_nsec += ctx->rto.tv_nsec;

            /* ensures no nanosecond overflow */
            if (timeout->tv_nsec >= 1000000000) {
                timeout->tv_sec  += timeout->tv_nsec / 1000000000;
                timeout->tv_nsec = timeout->tv_nsec % 1000000000;
            }
            if (timeout->tv_sec <= time(NULL))
                /*earliest unacked packet has timed out, unless it's currently sitting in buffer */
                eventflag = NETWORK_DATA|TIMEOUT;
        }
        else {
            timeout = NULL;
            DEBUG("No timeout set\n");
        }



        /* see stcp_api.h or stcp_api.c for details of this function */
        /* XXX: you will need to change some of these arguments! */
        event = stcp_wait_for_event(sd, eventflag, timeout);

        /* check whether it was the network, app, or a close request */
        if (event & APP_DATA)
        {
            DEBUG("Application Data\n");
            /* the application has requested that data be sent */
            /* we should send as long as send_wind > 0 */
            int open_window = ctx->send_wind - (ctx->send_next - ctx->send_unack);
            if(open_window > 0)
            {
                int send_size;

                /* only read in as much from app as we can send */
                if(open_window > MAXLEN) send_size = MAXLEN;
                else send_size = open_window;

                void* buffer = (char *) calloc(1, send_size);

                tcplen = stcp_app_recv(sd, buffer, send_size);

                if( tcplen > 0 ) {
                    DEBUG("Application data size: %d\n", tcplen);
                    /*create and send packet*/
                    packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, tcplen);
                    memcpy(packtosend + sizeof(STCPHeader), buffer, tcplen);
                    stcp_network_send(sd, packtosend, sizeof(STCPHeader) + tcplen, NULL);
                    DEBUG("Packet of payload size %d, ack number %d, seq number %d sent to network\n", tcplen, ctx->recv_next, ctx->send_next);
                    packet_t_create(ctx, packtosend, sizeof(STCPHeader) + tcplen); /* now a packet has been pushed onto the unacked queue */
                    /* update window length and send_next */
                    ctx->send_next += tcplen;
                }
                free(buffer);
                buffer = NULL;
                free(packtosend);
                packtosend = NULL;
            }
            else DEBUG("Could not get application data\n");
        }

        if (event & NETWORK_DATA)
        {
            DEBUG("Network Data\n");
            in_header = malloc(sizeof(STCPHeader));

            /* network data transmission */
            int data_size = recv_packet(sd, ctx, payload, MAXLEN, in_header);

            DEBUG("Received net data size is %d\n", data_size);


            /* send ACK as long as it's not past our window, and actually contained data */
            if(data_size > 0 || data_size == -2) { /* -2 indicates it received old data, which we should not send to the application */
                packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0);
                stcp_network_send(sd, packtosend, sizeof(STCPHeader), NULL);
                free(packtosend);
                packtosend = NULL;
            }



            /* send payload to application, if it's valid */
            if(data_size > 0) {
                DEBUG("Sent data of size %d to application\n", data_size);
                stcp_app_send(sd, ctx->recv_buffer, data_size);


                /* slide the window over */
                indicpt = strchr(ctx->recv_indicator, '\0');
                data_size = indicpt - ctx->recv_indicator;
                memcpy(tempbuff, ctx->recv_buffer + data_size, WINLEN - data_size);
                memset(ctx->recv_buffer, '\0', WINLEN);
                memcpy(ctx->recv_buffer, tempbuff, WINLEN - data_size);

                /* slide window indicator over */
                memcpy(tempbuff, indicpt, WINLEN - data_size);
                memset(ctx->recv_indicator, '\0', WINLEN);
                memcpy(ctx->recv_indicator, tempbuff, WINLEN - data_size);
            }

            /* deal with connection teardown, if need be */
            if (in_header->th_flags & TH_ACK) {
                /* go from FIN-WAIT1 --> FIN-WAIT2 */
                if(ctx->connection_state == CSTATE_FIN_WAIT1) {
                    DEBUG("State: FIN-WAIT2\n");
                    ctx->connection_state = CSTATE_FIN_WAIT2;
                }
                /* go from LAST-ACK --> CLOSED */
                if(ctx->connection_state == CSTATE_LAST_ACK) {
                    DEBUG("State: CLOSED\n");
                    ctx->connection_state = CSTATE_CLOSED;
                    free(in_header);
                    in_header = NULL;
                    break;
                }
                /* go from CLOSING --> CLOSED */
                if(ctx->connection_state == CSTATE_CLOSING) {
                    DEBUG("State: CLOSED\n");
                    ctx->connection_state = CSTATE_CLOSED;
                    free(in_header);
                    in_header = NULL;
                    break;
                }
            }
            /* branching for the receipt of a FIN packet */
            if (in_header->th_flags & TH_FIN) {
                DEBUG("Received FIN packet\n");
                /* Acknowledge FIN, which counts as a byte */
                ctx->recv_next++;
                packtosend = (void *)make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0);
                stcp_network_send(sd, packtosend, sizeof(STCPHeader), NULL);

                /* go into CLOSE-WAIT */
                if (ctx->connection_state == CSTATE_ESTABLISHED) {
                    DEBUG("State: CLOSE_WAIT\n");
                    /* inform app of FIN */
                    stcp_fin_received(sd);

                    ctx->connection_state = CSTATE_CLOSE_WAIT;
                }
                /* go from FIN-WAIT2 --> CLOSED */
                if (ctx->connection_state == CSTATE_FIN_WAIT2) {
                    DEBUG("State: CLOSED\n");
                    ctx->connection_state = CSTATE_CLOSED;
                    free(in_header);
                    in_header = NULL;
                    break;
                }
                /* go from FIN-WAIT1 --> CLOSING */
                if (ctx->connection_state == CSTATE_FIN_WAIT1) {
                    DEBUG("State: CLOSING\n");
                    /* inform app of FIN */
                    stcp_fin_received(sd);

                    ctx->connection_state = CSTATE_CLOSING;
                }
                free(in_header);
                in_header = NULL;
            }
        }

        if (event & APP_CLOSE_REQUESTED)
        {
            DEBUG("Application close requested\n");
            if(ctx->connection_state == CSTATE_ESTABLISHED)
            {
                /* need to send all outstanding data first */
                DEBUG("Sending FIN packet with seq %d, ack %d\n", ctx->send_next, ctx->recv_next);
                STCPHeader *header = make_stcp_packet(TH_FIN, ctx->send_next, ctx->recv_next, 0);
                stcp_network_send(sd, header, sizeof(STCPHeader), NULL);
                ctx->send_unack += 1;
                packet_t_create(ctx, header, sizeof(STCPHeader));
                free(header);
                header = NULL;
                /* go into FIN-WAIT1 */
                ctx->connection_state = CSTATE_FIN_WAIT1;
                DEBUG("State: FIN-WAIT1\n");
            }
            if(ctx->connection_state == CSTATE_CLOSE_WAIT)
            {
                DEBUG("Sending FIN packet with seq %d, ack %d\n", ctx->send_next, ctx->recv_next);
                STCPHeader *header = make_stcp_packet(TH_FIN, ctx->send_next, ctx->recv_next, 0);
                stcp_network_send(sd, header, sizeof(STCPHeader), NULL);
                packet_t_create(ctx, header, sizeof(STCPHeader));
                ctx->send_next += 1;
                free(header);
                header = NULL;
                /* go from CLOSE-WAIT --> LAST-ACK */
                ctx->connection_state = CSTATE_LAST_ACK;
                DEBUG("State: LAST-ACK\n");
            }
        }
        if (event == TIMEOUT)
        {
            int giveup = 0;
            /* TIMEOUT--resend all packets in ctx->unackd_packets */
            packet_t *resendpack;
            list_iterator_start(ctx->unackd_packets);
            while (list_iterator_hasnext(ctx->unackd_packets)) {
                resendpack = (packet_t *)list_iterator_next(ctx->unackd_packets);
                if (resendpack->retry_count > 6) {
                    /*close the connection*/
                    DEBUG("Too many retries");
                    errno = ECONNABORTED;
                    giveup = 1;
                }
                /* increment retries */
                resendpack->retry_count++;
                clock_gettime(CLOCK_REALTIME, &(resendpack->start_time));
                ((STCPHeader *)(resendpack->packet))->th_ack = htonl(ctx->recv_next);
                stcp_network_send(sd, resendpack->packet, resendpack->packet_size, NULL);
                DEBUG("Resent packet with sequence number %d\n", ntohl(((STCPHeader *)(resendpack->packet))->th_seq));
            }
            list_iterator_stop(ctx->unackd_packets);
            if (giveup)
                break;
        }
    }
}
示例#3
0
int handle_cstate_est_recv(mysocket_t sd, context_t * ctx){

    our_dprintf("* IN EST REC\n");
    size_t len =  sizeof(struct tcphdr) + STCP_MSS;
    uint8_t buff[len];
    uint32_t data_len, data_index;
    size_t delta, recv_len, recv_packet_len;
    recv_len = stcp_network_recv(sd, buff, len);
    struct tcphdr * tcp_packet  = (struct tcphdr *) buff;
    size_t hdr_size = tcp_packet->th_off * 4;
    recv_packet_len = recv_len - 4 * tcp_packet->th_off;

    /* check if any data was ack'd */
    if (tcp_packet->th_flags & TH_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;
        }
        ctx->sent_last_byte_acked = ntohl(tcp_packet->th_ack);
        //our_dprintf("****got an ack: %u\n", tcp_packet->th_ack);
    }
    /* check to see if the seq number is appropriate */
    if (ntohl(tcp_packet->th_seq) != ctx->recd_next_byte_expected){
        //our_dprintf("unexpected seq. rec seq : %u, expected : %u\n", tcp_packet->th_seq, ctx->recd_next_byte_expected);

        /* if part of the data is below the seq window */
        if ((ntohl(tcp_packet->th_seq) < ctx->recd_next_byte_expected) && 
            (ntohl(tcp_packet->th_seq) + recv_packet_len > ctx->recd_next_byte_expected)){
            //our_dprintf("some data salvageable\n");
            /* some of the data should be salvaged */
            data_len = ntohl(tcp_packet->th_seq) + recv_packet_len - ctx->recd_next_byte_expected;
            data_index = recv_packet_len - data_len;
        } else if (0) {
            /* placeholder for if data overflows upper bound of sliding window */

        } else {
            //our_dprintf("bad packet\n");
            return 0;
        }
    } else {
        data_len = recv_packet_len;
        data_index = 0;
    }
    uint8_t * data = buff + hdr_size; 
    uint32_t wind_index = ((ctx->recd_last_byte_recd + 1) - ctx->initial_recd_seq_num) % MAX_WINDOW_SIZE;  
    //our_dprintf("window index %u, data len %u\n", wind_index, data_len); 
    //our_dprintf("received data: %s\n", data);
    if (wind_index + data_len > MAX_WINDOW_SIZE){
        /* we're wrapping the buffer */
        delta = MAX_WINDOW_SIZE - wind_index;
        //our_dprintf("wrapping recv buff \n");
        /*copy data to ctx->buffer and send it to app */ 
        memcpy(ctx->recv_wind + wind_index, data + data_index, delta); 
        stcp_app_send( sd, ctx->recv_wind + wind_index, delta);

        memcpy(ctx->recv_wind, data + delta + data_index, data_len - delta);
        stcp_app_send( sd, ctx->recv_wind, data_len - delta);
        
    } else {
        /* we're not wrapping the buffer */
        //our_dprintf("don't need to wrap, data len %d\n", data_len);
        /*copy data to ctx->buffer and send it to app */
        memcpy(ctx->recv_wind + wind_index, data + data_index, data_len);
        stcp_app_send( sd, ctx->recv_wind + wind_index, data_len);
    }

    ctx->recd_last_byte_recd += data_len;
    ctx->recd_next_byte_expected += data_len;
    ctx->recd_last_byte_read += data_len;

    if (data_len > 0 ) {
        //our_dprintf("acking %u bytes\n", data_len);
        send_syn_ack_fin(sd, ctx, SEND_ACK, 0, ctx->recd_next_byte_expected);
    } else {
        //our_dprintf("** flags %u\n", tcp_packet->th_flags);
    }
    

    if (tcp_packet->th_flags & TH_FIN) {
        stcp_fin_received(sd);
        if (data_len == 0){
            ctx->recd_last_byte_recd++;
            ctx->recd_next_byte_expected++;
            ctx->recd_last_byte_read++;
        }
        send_syn_ack_fin(sd, ctx, SEND_ACK, 0, ctx->recd_next_byte_expected);
        ctx->connection_state = CSTATE_CLOSE_WAIT;
        close_tcp_conn(sd, ctx);
    }
    return 0;
}