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; }
static void transport_send_data(mysocket_t sd, char* data, int len, context_t* ctx)//LAN { strncpy(send_buffer, data); ctx.next_seq += len; ssize_t result = stcp_network_send(sd, send_buffer, sizeof(send_buffer), NULL); //send real app data or stub data }
/* *************************************************** * Function: network_send * *************************************************** * Wrapper function for sending network data. It converts header fields * into network byte-order but then changes them back before returning * in case the caller plans to use that packet later on. */ ssize_t network_send(mysocket_t sd, const void *src, size_t src_len) { struct tcphdr* hdr = (struct tcphdr *)src; hdr->th_ack = htonl(hdr->th_ack); hdr->th_seq = htonl(hdr->th_seq); hdr->th_win = htons(hdr->th_win); hdr->th_flags = hdr->th_flags; hdr->th_off = hdr->th_off; ssize_t result; while ((result = stcp_network_send(sd, src, src_len, NULL))<0); hdr->th_ack = ntohl(hdr->th_ack); hdr->th_seq = ntohl(hdr->th_seq); hdr->th_win = ntohs(hdr->th_win); return result; }
static void transport_send_head(mysocket_t sd, int hflag, context_t* ctx)//LAN { struct tcphdr head; //fill a ACK header head.th_flags = hflag; head.th_seq = ;//unsent seq number head.th_ack = ctx->next_seq;//ctx->next_seq; head.th_win = ;//??? head.th_seq = htons(head.th_seq); head.th_ack = htons(head.th_ack); head.th_win = htons(head.th_win); ssize_t result = stcp_network_send(sd, send_buffer, sizeof(struct tcphdr), NULL); //use htonl htons to codec }
int send_syn_ack_fin(mysocket_t sd, context_t * ctx, uint8_t to_send_flags, tcp_seq seq_num, tcp_seq ack_num) { size_t len = sizeof(struct tcphdr); uint8_t buff[len]; struct tcphdr * tcp_packet = (struct tcphdr *) buff; /* th_sport, th_dport, th_sum are set by network layer */ /* th_urg is ignored by stcp*/ if (to_send_flags & SEND_SYN) { tcp_packet->th_seq = htonl(seq_num); tcp_packet->th_flags |= TH_SYN; ctx->sent_last_byte_written = seq_num; ctx->sent_last_byte_sent = seq_num; } else { tcp_packet->th_seq = htonl(ctx->sent_last_byte_sent + 1); } if (to_send_flags & SEND_ACK) { tcp_packet->th_ack = htonl(ack_num); tcp_packet->th_flags |= TH_ACK; //our_dprintf("sending ack %u\n", ack_num); } if (to_send_flags & SEND_FIN) { tcp_packet->th_flags |= TH_FIN; ctx->sent_last_byte_sent++; ctx->sent_last_byte_written++; } tcp_packet->th_off = 5; /* no options, data begins 20 bytes into packet */ /* set adv window size */ tcp_packet->th_win = htons(calc_adv_wind(ctx)); ctx->sent_adv_window = calc_adv_wind(ctx); /* send the newly constructed packet */ ssize_t n = stcp_network_send(sd, buff , len, 0); if (n == -1){ fprintf(stderr,"error: client bad send\n"); return -1; } return 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 = ×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; } } }
/* 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; uint8_t flags; int tcplen; ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); /* ensures this array begins as entirely null characters */ memset(ctx->recv_indicator, '\0', WINLEN); char buffer[sizeof(STCPHeader) + MAXLEN]; generate_initial_seq_num(ctx); ctx->connection_state = CSTATE_CLOSED; list_t unackd; ctx->unackd_packets = &unackd; list_init(ctx->unackd_packets); /* used for setting timeouts */ struct timespec timestart; struct timespec *timeout; /* 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. */ if(is_active) { ctx->send_unack = ctx->initial_sequence_num; STCPHeader *header = make_stcp_packet(TH_SYN, ctx->send_unack, 0, 0); /* first step in the handshake: sending a SYN packet */ tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); ctx->send_next = ctx->send_unack; /* this ensures the SYN packet will be resent if it is dropped/times out */ packet_t_create(ctx, header, sizeof(STCPHeader)); DEBUG("Sent SYN packet with seq number %d\n", ntohl(header->th_seq)); if(tcplen < 0) { DEBUG("SYN send failed"); errno = ECONNREFUSED; } else { ctx->send_next++; ctx->connection_state = CSTATE_SYN_SENT; /* the client now waits for a SYN/ACK */ while(ctx->connection_state == CSTATE_SYN_SENT) { timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time; timeout = ×tart; timeout->tv_sec += ctx->rto.tv_sec; timeout->tv_nsec += ctx->rto.tv_nsec; if (timeout->tv_nsec >= 1000000000) { timeout->tv_sec += timeout->tv_nsec / 1000000000; timeout->tv_nsec = timeout->tv_nsec % 1000000000; } unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA, timeout); /* yes we need to add timeout later */ if(event & NETWORK_DATA) { /* we now expect the next packet to be a SYN-ACK */ tcplen = recv_packet(sd, ctx, NULL, 0, header); flags = (TH_SYN|TH_ACK); if( tcplen < 0 || !((header->th_flags & TH_SYN)&&(header->th_flags & TH_ACK))) { DEBUG("Did not receive SYN/ACK\n"); continue; } else { DEBUG("packet received with seq %d and ack %d\n", ntohl(header->th_seq), ntohl(header->th_ack)); if(ntohl(header->th_ack) == ctx->send_next) { header = make_stcp_packet(TH_ACK, ctx->send_next, ctx->recv_next, 0); tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); /* the client now sends an ACK packet, and goes to the ESTABLISHED state */ DEBUG("Sent ACK packet with seq number %d\n", ntohl(header->th_seq)); if(tcplen < 0) { DEBUG("ACK send failed"); errno = ECONNABORTED; break; } ctx->connection_state = CSTATE_ESTABLISHED; free(header); header = NULL; } } } } } } if(!is_active) { /* wait for a SYN packet */ while(ctx->connection_state == CSTATE_CLOSED) { ctx->send_unack = ctx->initial_sequence_num; ctx->send_next = ctx->send_unack; unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA|TIMEOUT, NULL); if(event & NETWORK_DATA) { STCPHeader *header = (STCPHeader *) malloc(sizeof(STCPHeader)); tcplen = recv_packet(sd, ctx, NULL, 0, header); if(tcplen < 0 || header->th_flags != TH_SYN) { DEBUG("Did not receive SYN packet\n"); continue; } else { DEBUG("Received SYN with seq number %d\n", ntohl(header->th_seq)); ctx->connection_state = CSTATE_SYN_RECEIVED; header = make_stcp_packet(TH_SYN|TH_ACK, ctx->send_unack, ctx->recv_next, 0); tcplen = stcp_network_send(sd, header, sizeof(STCPHeader), NULL); packet_t_create(ctx, header, sizeof(STCPHeader)); /* send out a SYN/ACK packet to the initiating client */ DEBUG("SYN/ACK sent with ack number %d and seq number %d\n", htonl(header->th_ack), htonl(header->th_seq)); if(tcplen < 0) { DEBUG("SYN/ACK send failed"); errno = ECONNABORTED; break; } /* ensures the SYN-ACK will be resent if it is not ACK'd */ packet_t_create(ctx, header, sizeof(STCPHeader)); ctx->send_next++; } } } while(ctx->connection_state == CSTATE_SYN_RECEIVED) { timestart = ((packet_t *)list_get_at(ctx->unackd_packets, 0))->start_time; timeout = ×tart; timeout->tv_sec += ctx->rto.tv_sec; timeout->tv_nsec += ctx->rto.tv_nsec; if (timeout->tv_nsec >= 1000000000) { timeout->tv_sec += timeout->tv_nsec / 1000000000; timeout->tv_nsec = timeout->tv_nsec % 1000000000; } unsigned int event = stcp_wait_for_event(sd, NETWORK_DATA, timeout); if(event & NETWORK_DATA) { STCPHeader *header = (STCPHeader *) buffer; tcplen = recv_packet(sd, ctx, NULL, 0, header); if(tcplen < 0 || !(header->th_flags & TH_ACK)) { DEBUG("Did not receive ACK packet\n"); continue; } /* if it's the right ACK packet, go to the ESTABLISHED state */ if(ntohl(header->th_ack) == ctx->send_next) { DEBUG("Received ACK with ack number %d\n", ntohl(header->th_ack)); ctx->connection_state = CSTATE_ESTABLISHED; } } } } if(ctx->connection_state == CSTATE_ESTABLISHED) DEBUG("State: seq %d ack %d send window %d\n", ctx->send_next, ctx->recv_next, ctx->send_wind); /* unblocks the application */ /* relays possible error conditions */ stcp_unblock_application(sd); control_loop(sd, ctx); /* do any cleanup here */ free(ctx); }