/* *************************************************** * 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); }
/* 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; }