/* *************************************************** * 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; } }
/* 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 = ×tart; /* 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; } } }
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; }