/* *************************************************** * Function: init_ctx * *************************************************** * Initiate the connection context structure */ static context_t* init_ctx(bool_t is_active) { srand((unsigned)time(0)); context_t *ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); ctx->connection_state = CSTATE_CLOSED; generate_initial_seq_num(ctx); if (is_active){ /* Because time(0) can be the same for client/server due to low * granularity, call it again for one side */ generate_initial_seq_num(ctx); } init_window(&(ctx->sbuffer)); init_window(&(ctx->rbuffer)); ctx->estimated_rtt = 0; ctx->devrtt = 0; ctx->rto = INIT_RTO; return ctx; }
/* 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); }
/* 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; ctx = (context_t *) calloc(1, sizeof(context_t)); assert(ctx); generate_initial_seq_num(ctx); ctx->connection_state = CSTATE_CLOSED; //init all buffer /* 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 (!transport_3way_handshake(sd, ctx)) { stcp_unblock_application(sd); free(ctx); return; } //init send and recv buffer here ctx->connection_state = CSTATE_ESTABLISHED; stcp_unblock_application(sd); control_loop(sd, ctx); /* do any cleanup here */ free(ctx); }
/* 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); }