/* Cancel discovery if it is in progress */ static void miniroute_discovery_cancel() { if (discovery_alarm > -1) { deregister_alarm(discovery_alarm); discovery_alarm = -1; semaphore_V(discovery_sig); } }
int wait_for_arrival_or_timeout(semaphore_t sema, alarm_t* alarm, int timeout) { if (sema == NULL) { fprintf(stderr, "ERROR: wait_for_arrival_or_timeout() passed uninitialized semaphore\n"); return -1; } *alarm = (alarm_t) register_alarm(timeout, (alarm_handler_t) semaphore_V, (void*) sema); semaphore_P(sema); // fprintf(stderr, "Woke up!\n"); if (*alarm != NULL) { return deregister_alarm((alarm_id) (*alarm)); } else { return 0; } }
/* * This is the clock interrupt handling routine. * You have to call minithread_clock_init with this * function as parameter in minithread_system_initialize */ void clock_handler(void* arg) { interrupt_level_t old_level = set_interrupt_level(DISABLED); nInterrupts++; // get_next_alarm always runs in O(1) alarm_t *next_alarm = get_next_alarm(); while (next_alarm) { call_handler(next_alarm); // since next_alarm is the first element, deregister also runs in O(1) deregister_alarm(next_alarm); next_alarm = get_next_alarm(); } implement_scheduler(); set_interrupt_level(old_level); }
void minisocket_close(minisocket_t *socket) { if (socket) { if (socket->socket_state == CLOSING) { socket->socket_state = WAITING_SYN; return; } if (socket->socket_state == CLOSED) { minisocket_free(socket); return; } minisocket_error s_error; int wait_val = 100; while (wait_val <= 12800) { send_control_message(MSG_FIN, socket->remote_port, socket->remote_addr, socket->local_port, socket->seq_number, socket->ack_number, &s_error); socket->seq_number += 1; if (s_error == SOCKET_OUTOFMEMORY) { return; } alarm_id a = register_alarm(wait_val, (alarm_handler_t) semaphore_V, socket->wait_for_ack); semaphore_P(socket->wait_for_ack); interrupt_level_t old_level = set_interrupt_level(DISABLED); if (socket->ack_flag == 0) { wait_val *= 2; socket->seq_number--; set_interrupt_level(old_level); continue; } else if (socket->ack_flag == 1) { deregister_alarm(a); minisocket_free(socket); set_interrupt_level(old_level); return; } } } semaphore_V(socket->send_receive_mutex); }
/* * Send a message to the other end of the socket. * * The send call should block until the remote host has ACKnowledged receipt of * the message. This does not necessarily imply that the application has called * 'minisocket_receive', only that the packet is buffered pending a future * receive. * * It is expected that the order of calls to 'minisocket_send' implies the order * in which the concatenated messages will be received. * * 'minisocket_send' should block until the whole message is reliably * transmitted or an error/timeout occurs * * Arguments: the socket on which the communication is made (socket), the * message to be transmitted (msg) and its length (len). * Return value: returns the number of successfully transmitted bytes. Sets the * error code and returns -1 if an error is encountered. * * Comments: invalid params for socket, msg but len = 0 case is ok */ int minisocket_send(minisocket_t socket, minimsg_t msg, int len, minisocket_error *error) { unsigned int num_pkt; unsigned int i; interrupt_level_t l; resend_arg* resend_alarm_arg; unsigned int max_size; //printf("in minisocket_send\n"); //max size for PAYLOAD, not entire packet max_size = MAX_NETWORK_PKT_SIZE - sizeof(struct mini_header_reliable); num_pkt = len / max_size + 1; //number of divided packets //printf("gonna send %d packet(s)!\n", num_pkt); //error checking if (!socket || sock_array[socket->src_port] == NULL || msg == NULL || len < 0) { *error = SOCKET_INVALIDPARAMS; return -1; } //allow this if (len == 0){ return 0; } //check for invalid cases l = set_interrupt_level(DISABLED); if (socket->curr_state != CONNECTED){ if (socket->curr_state == CLOSE_RCV){ *error = SOCKET_BUSY; set_interrupt_level(l); return -1; } *error = SOCKET_SENDERROR; set_interrupt_level(l); return -1; } //try sending *error = SOCKET_NOERROR; (socket->curr_seq)++; minisocket_send_data(socket,len>max_size ? max_size:len,msg, error); //printf("sent first packet!\n"); if (*error != SOCKET_NOERROR){ set_interrupt_level(l); return -1; } resend_alarm_arg = (resend_arg*)calloc(1, sizeof(resend_arg)); //otherwise register an alarm for resending and proceed resend_alarm_arg->sock = socket; resend_alarm_arg->msg_type = MSG_ACK; resend_alarm_arg->data_len = len>max_size?max_size:len; resend_alarm_arg->data = msg; //placeholder resend_alarm_arg->error = error; socket->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); socket->resend_alarm = NULL; } socket->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, &resend_alarm_arg, minithread_time()); socket->curr_state = MSG_WAIT; set_interrupt_level(l); while (i < num_pkt){ semaphore_P(socket->ack_ready_sem); l = set_interrupt_level(DISABLED); //packet got acked, deregister alarm from before if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); socket->resend_alarm = NULL; } switch (socket->curr_state){ case CLOSE_SEND: //closing connection semaphore_V(socket->ack_ready_sem); //notify close thread *error = SOCKET_SENDERROR; set_interrupt_level(l); free(resend_alarm_arg); return i * max_size; case CONNECTED: i++; if (i >= num_pkt){ //we sent everything set_interrupt_level(l); break; } (socket->curr_seq)++; minisocket_send_data(socket, i<num_pkt-1? max_size : len-(max_size*i), ((char*)msg) + i*max_size, error); if (*error != SOCKET_NOERROR){//TODO:may not need to do this set_interrupt_level(l); free(resend_alarm_arg); return -1; } //otherwise register an alarm for resending and proceed resend_alarm_arg->sock = socket; resend_alarm_arg->msg_type = MSG_ACK; resend_alarm_arg->data_len = i<num_pkt-1? max_size : len-(max_size*i); resend_alarm_arg->data = ((char*)msg) + i*max_size; resend_alarm_arg->error = error; if (socket->resend_alarm) { deregister_alarm(socket->resend_alarm); socket->resend_alarm = NULL; } socket->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); socket->curr_state = MSG_WAIT; set_interrupt_level(l); break; default: // error case, simply return *error = SOCKET_SENDERROR; set_interrupt_level(l); free(resend_alarm_arg); return i * max_size; } } //we sent all the packets! *error = SOCKET_NOERROR; //printf("return on send SUCCESS!\n"); //printf("exiting send with socket at state %d\n", socket->curr_state); free(resend_alarm_arg); return len; }
/* Initiate the communication with a remote site. When communication is * established create a minisocket through which the communication can be made * from now on. * * The first argument is the network address of the remote machine. * * The argument "port" is the port number on the remote machine to which the * connection is made. The port number of the local machine is one of the free * port numbers. * * Return value: the minisocket_t created, otherwise NULL with the errorcode * stored in the "error" variable. */ minisocket_t minisocket_client_create(network_address_t addr, int port, minisocket_error *error) { minisocket_t new_sock; interrupt_level_t l; resend_arg* resend_alarm_arg; unsigned short start; char tmp; //check valid portnum if (port < 0 || port >= CLIENT_START) { *error = SOCKET_INVALIDPARAMS; return NULL; } semaphore_P(client_lock); start = curr_client_idx; while (sock_array[curr_client_idx] != NULL){ curr_client_idx++; if (curr_client_idx >= NUM_SOCKETS){ curr_client_idx = CLIENT_START; } if (curr_client_idx == start){ // client sockets full semaphore_V(client_lock); *error = SOCKET_NOMOREPORTS; return NULL; } } new_sock = (minisocket_t)malloc(sizeof(struct minisocket)); if (!new_sock){ semaphore_V(client_lock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_ready_sem = semaphore_create(); if (!(new_sock->pkt_ready_sem)){ semaphore_V(client_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_q = queue_new(); if (!(new_sock->pkt_q)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->sock_lock = semaphore_create(); if (!(new_sock->sock_lock)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->ack_ready_sem = semaphore_create(); if (!(new_sock->ack_ready_sem)){ semaphore_V(client_lock); semaphore_destroy(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); semaphore_destroy(new_sock->sock_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } semaphore_initialize(new_sock->pkt_ready_sem, 0); semaphore_initialize(new_sock->ack_ready_sem, 0); semaphore_initialize(new_sock->sock_lock, 1); new_sock->curr_state = CONNECT_WAIT; new_sock->try_count = 0; new_sock->curr_ack = 0; new_sock->curr_seq = 1; new_sock->resend_alarm = NULL; //no alarm set new_sock->src_port = curr_client_idx; new_sock->dst_port = port; network_address_copy(addr, new_sock->dst_addr); sock_array[curr_client_idx] = new_sock; semaphore_V(client_lock); l = set_interrupt_level(DISABLED); minisocket_send_ctrl(MSG_SYN, new_sock, error); resend_alarm_arg = (resend_arg*)calloc(1, sizeof(resend_arg)); resend_alarm_arg->sock = new_sock; resend_alarm_arg->msg_type = MSG_SYN; resend_alarm_arg->data_len = 0; resend_alarm_arg->data = &tmp; //placeholder resend_alarm_arg->error = error; new_sock->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); set_interrupt_level(l); semaphore_P(new_sock->ack_ready_sem); l = set_interrupt_level(DISABLED); switch(new_sock->curr_state) { case CONNECTED: //we are connected // must have gotten a MSG_SYNACK new_sock->curr_state = CONNECTED; new_sock->try_count = 0; if (new_sock->resend_alarm){ deregister_alarm(new_sock->resend_alarm); } new_sock->resend_alarm = NULL; *error = SOCKET_NOERROR; set_interrupt_level(l); break; case CLOSE_RCV: *error = SOCKET_BUSY; minisocket_destroy(new_sock, error); if (new_sock->resend_alarm){ deregister_alarm(new_sock->resend_alarm); } new_sock->resend_alarm = NULL; *error = SOCKET_BUSY; set_interrupt_level(l); new_sock = NULL; break; default: // error *error = SOCKET_NOSERVER; minisocket_destroy(new_sock, error); set_interrupt_level(l); new_sock = NULL; break; } free(resend_alarm_arg); return new_sock; }
/* Listen for a connection from somebody else. When communication link is * created return a minisocket_t through which the communication can be made * from now on. * * The argument "port" is the port number on the local machine to which the * client will connect. * * Return value: the minisocket_t created, otherwise NULL with the errorcode * stored in the "error" variable. */ minisocket_t minisocket_server_create(int port, minisocket_error *error) { minisocket_t new_sock; interrupt_level_t l; network_interrupt_arg_t * pkt; resend_arg* resend_alarm_arg; char tmp; //check valid portnum if (port < 0 || port >= CLIENT_START){ *error = SOCKET_INVALIDPARAMS; return NULL; } //check port in use semaphore_P(server_lock); //printf("calling server_create at port %d.\n", port); //printf("value at port %d is %li.\n", port, (long)sock_array[port]); if (sock_array[port] != NULL){ *error = SOCKET_PORTINUSE; return NULL; } new_sock = (minisocket_t)malloc(sizeof(struct minisocket)); if (!new_sock){ semaphore_V(server_lock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_ready_sem = semaphore_create(); if (!(new_sock->pkt_ready_sem)){ semaphore_V(server_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->pkt_q = queue_new(); if (!(new_sock->pkt_q)){ semaphore_V(server_lock); semaphore_destroy(new_sock->pkt_ready_sem); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->sock_lock = semaphore_create(); if (!(new_sock->sock_lock)){ semaphore_V(server_lock); free(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } new_sock->ack_ready_sem = semaphore_create(); if (!(new_sock->ack_ready_sem)){ semaphore_V(server_lock); semaphore_destroy(new_sock->pkt_ready_sem); queue_free(new_sock->pkt_q); semaphore_destroy(new_sock->sock_lock); free(new_sock); *error = SOCKET_OUTOFMEMORY; return NULL; } semaphore_initialize(new_sock->pkt_ready_sem, 0); semaphore_initialize(new_sock->ack_ready_sem, 0); semaphore_initialize(new_sock->sock_lock, 1); new_sock->curr_state = LISTEN; new_sock->try_count = 0; new_sock->curr_ack = 0; new_sock->curr_seq = 1; new_sock->resend_alarm = NULL; // no alarm set new_sock->src_port = port; new_sock->dst_port = -1; // not paired with client network_address_blankify(new_sock->dst_addr); // not paired with client sock_array[port] = new_sock; semaphore_V(server_lock); resend_alarm_arg = (resend_arg*)calloc(1, sizeof(resend_arg)); while (1) { // wait for MSG_SYN semaphore_P(new_sock->ack_ready_sem); l = set_interrupt_level(DISABLED); switch (new_sock->curr_state) { case CONNECTING: minisocket_send_ctrl(MSG_SYNACK, new_sock, error); resend_alarm_arg->sock = new_sock; resend_alarm_arg->msg_type = MSG_SYNACK; resend_alarm_arg->data_len = 0; resend_alarm_arg->data = &tmp; //placeholder resend_alarm_arg->error = error; new_sock->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); new_sock->curr_state = MSG_WAIT; set_interrupt_level(l); break; case CONNECTED: // must have gotten a MSG_ACK //printf("in server_create, SUCCESS!\n"); new_sock->try_count = 0; deregister_alarm(new_sock->resend_alarm); new_sock->resend_alarm = NULL; *error = SOCKET_NOERROR; set_interrupt_level(l); //printf("exiting server_create\n"); free(resend_alarm_arg); return new_sock; break; case EXIT: default: *error = SOCKET_SENDERROR; // clean out the queue while (queue_dequeue(new_sock->pkt_q,(void**)&pkt) != -1){ free(pkt); } new_sock->curr_state = LISTEN; new_sock->curr_ack = 0; new_sock->curr_seq = 1; set_interrupt_level(l); break; } } free(resend_alarm_arg); return new_sock; }
/* Takes in a routing packet and does error checking. * Adds it to the cache if this packet was destined for us. * Returns 1 if this packet has data to be passed along, * O otherwise. */ int miniroute_process_packet(network_interrupt_arg_t* pkt) { struct routing_header* pkt_hdr = NULL; network_address_t tmp_addr; network_address_t src_addr; network_address_t dst_addr; network_address_t nxt_addr; unsigned int discovery_pkt_id; unsigned int pkt_ttl; unsigned int path_len; miniroute_t path = NULL; miniroute_t new_path = NULL; network_address_t* new_route = NULL; unsigned int i; unsigned int found; struct routing_header hdr; char tmp; dcb_t control_block; //printf("entering miniroute_process_packet\n"); if (pkt == NULL || pkt->size < sizeof(struct routing_header)) { //printf("exiting miniroute_process_packet on INVALID PARAMS\n"); return 0; } pkt_hdr = (struct routing_header*)pkt->buffer; unpack_address(pkt_hdr->destination, dst_addr); discovery_pkt_id = unpack_unsigned_int(pkt_hdr->id); pkt_ttl = unpack_unsigned_int(pkt_hdr->ttl); path_len = unpack_unsigned_int(pkt_hdr->path_len); unpack_address(pkt_hdr->path[0], src_addr); if (network_compare_network_addresses(my_addr, dst_addr)) { //same if (!miniroute_cache_get(route_cache, src_addr)) { //not in cache if (pkt_hdr->routing_packet_type == ROUTING_ROUTE_DISCOVERY) { //add myself to the path vector pack_address(pkt_hdr->path[path_len], my_addr); path_len++; pack_unsigned_int(pkt_hdr->path_len, path_len); } new_route = (network_address_t*)calloc(path_len, sizeof(network_address_t)); if (new_route == NULL) { free(pkt); //printf("exiting miniroute_process_packet on CALLOC ERROR\n"); return 0; } for (i = 0; i < path_len; i++) { unpack_address(pkt_hdr->path[path_len - i - 1], tmp_addr); network_address_copy(tmp_addr, new_route[i]); } new_path = (miniroute_t)calloc(1, sizeof(struct miniroute)); if (new_path == NULL) { free(pkt); free(new_route); //printf("exiting miniroute_process_packet on CALLOC ERROR\n"); return 0; } new_path->route = new_route; new_path->len = path_len; miniroute_cache_put(route_cache, src_addr, new_path); } //added new route to cache } else if (pkt_ttl <= 0) { free(pkt); //printf("exiting miniroute_process_packet on TTL ERROR\n"); return 0; } else if (pkt_hdr->routing_packet_type != ROUTING_ROUTE_DISCOVERY) { //different //check from 2nd to second to last address found = 0; for (i = 1; i < path_len - 1; i++) { unpack_address(pkt_hdr->path[i], tmp_addr); if (network_compare_network_addresses(my_addr, tmp_addr)) { unpack_address(pkt_hdr->path[i+1], nxt_addr); found = 1; break; } } if (!found) { free(pkt); return 0; } } switch (pkt_hdr->routing_packet_type) { case ROUTING_DATA: //printf("got a DATA pkt\n"); if (network_compare_network_addresses(my_addr, dst_addr)) { //same //printf("exiting miniroute_process_packet on DATA PKT\n"); return 1; } else { //skip packet type, shouldn't change //skip destination, shouldn't change //skip id, shouldn't change pack_unsigned_int(pkt_hdr->ttl, pkt_ttl - 1); //subtract ttl network_send_pkt(nxt_addr, sizeof(struct routing_header), (char*)pkt_hdr, 0, &tmp); } break; case ROUTING_ROUTE_DISCOVERY: if (network_compare_network_addresses(my_addr, dst_addr)) { //printf("got a DISCOVERY pkt, for me\n"); //same path = miniroute_cache_get(route_cache, src_addr); hdr.routing_packet_type = ROUTING_ROUTE_REPLY; pack_address(hdr.destination, src_addr); pack_unsigned_int(hdr.id, discovery_pkt_id); pack_unsigned_int(hdr.ttl, MAX_ROUTE_LENGTH); pack_unsigned_int(hdr.path_len, path->len); for (i = 0; i < path->len; i++) { pack_address(hdr.path[i], path->route[i]); } network_send_pkt(path->route[1], sizeof(struct routing_header), (char*)(&hdr), 0, &tmp); } else { //printf("got a DISCOVERY pkt, for someone else\n"); //different //scan to check if i am in list //if yes then discard //else append to path vector and broadcast // //scan to check if i am in list //if yes then pass along, else discard for (i = 0; i < path_len - 1; i++) { unpack_address(pkt_hdr->path[i], tmp_addr); if (network_compare_network_addresses(my_addr, tmp_addr)) { free(pkt); // printf("exiting miniroute_process_packet on BROADCAST LOOP\n"); return 0; } } //printf("checks passed\n"); pack_address(pkt_hdr->path[path_len], my_addr); pack_unsigned_int(pkt_hdr->path_len, path_len + 1); //add path_len pack_unsigned_int(pkt_hdr->ttl, pkt_ttl - 1); //subtract ttl //printf("packet header configured\n"); //printf("my addr is (%i,%i)\n", my_addr[0], my_addr[1]); //printf("source addr is (%i,%i)\n", src_addr[0], src_addr[1]); //printf("dst addr is (%i,%i)\n", dst_addr[0], dst_addr[1]); //for (i = 0 ; i < path_len + 1; i++){ //unpack_address(pkt_hdr->path[i], tmp_addr); //printf("->(%i,%i)", tmp_addr[0], tmp_addr[1]); //} //printf("\n"); network_bcast_pkt(sizeof(struct routing_header), (char*)pkt_hdr, 0, &tmp); //send to neighbors //printf("broadcast successful\n"); } break; case ROUTING_ROUTE_REPLY: //printf("got a REPLY pkt\n"); if (network_compare_network_addresses(my_addr, dst_addr)) { //same control_block = hash_table_get(dcb_table, src_addr); if (control_block) { deregister_alarm(control_block->resend_alarm); control_block->resend_alarm = NULL; control_block->alarm_arg = NULL; semaphore_V(control_block->route_ready); } } else { //different //check ttl //scan to check if i am in list //if yes then pass along, else discard // //skip packet type, shouldn't change //skip destination, shouldn't change //skip id, shouldn't change pack_unsigned_int(pkt_hdr->ttl, pkt_ttl - 1); //subtract ttl network_send_pkt(nxt_addr, sizeof(struct routing_header), (char*)pkt_hdr, 0, &tmp); } break; default: //WTFFF??? break; } //printf("exiting miniroute_process_packet on SUCCESS\n"); free(pkt); return 0; }
void minisocket_close(minisocket_t *socket) { if (socket == NULL || mini_socket_data[socket->local_port_number] == NULL || socket->state == CLOSED) { return; // no cleanup necessary } fprintf(stderr, "Closing time \n"); mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_FIN); int send_success = 0; int timeout = START_TIMEOUT / 2; for (int i = 0; i < MAX_FAILURES; i++) { if (socket->state == CLOSED) { return; //received a MSG_FIN message in network_handler, so stop re-tries } timeout = timeout * 2; network_send_pkt(socket->remote_address, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!send_success && !alarm_fired) { semaphore_P(socket->ack_ready); //alarm has fired, since there are no packets to be received if (queue_length(socket->acknowledgements) == 0) { printf("CLOSE queue length: %d\n", queue_length(socket->acknowledgements)); alarm_fired = 1; //goes to next iteration of for loop } //There is a packet (alarm hasnt fired yet) else { network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // verify non null message if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; //already checked port and address in network_handler if (received_header->message_type == MSG_ACK) { deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found send_success = 1; free(interrupt_message); break; } else { free(interrupt_message); } } } } if (send_success) { break; } } printf("out of CLOSE\n"); //sender considers the connection closed, even if MSG_ACK not received if (socket->state != CLOSED) { printf("calling DESTROY from CLOSE\n"); minisocket_destroy(socket); } }
minisocket_t* minisocket_client_create(const network_address_t addr, int port, minisocket_error *error) { if (!addr || port < MIN_SERVER_PORT || port > MAX_SERVER_PORT) { *error = SOCKET_INVALIDPARAMS; return NULL; } int port_val = -1; // CHECK what n_client_ports does if (!ports[n_client_ports]) { port_val = n_client_ports; } else { for (int i = MIN_CLIENT_PORT; i <= MAX_CLIENT_PORT; i++) { if (!ports[i]) { port_val = i; break; } } } n_client_ports = port_val + 1; if (n_client_ports > MAX_CLIENT_PORT) { n_client_ports = 0; } if (port_val == -1) { *error = SOCKET_NOMOREPORTS; return NULL; } minisocket_t *new_socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (!new_socket) { *error = SOCKET_OUTOFMEMORY; return NULL; } new_socket->socket_state = INITIAL; semaphore_P(ports_mutex); ports[port_val] = new_socket; semaphore_V(ports_mutex); new_socket->socket_type = 'c'; network_address_copy(local_host, new_socket->local_addr); //network_get_my_address(new_socket->local_addr); new_socket->local_port = port_val; network_address_copy(addr, new_socket->remote_addr); new_socket->remote_port = port; new_socket->data = queue_new(); new_socket->data_ready = semaphore_create(); new_socket->wait_for_ack = semaphore_create(); new_socket->send_receive_mutex = semaphore_create(); semaphore_initialize(new_socket->data_ready, 0); semaphore_initialize(new_socket->wait_for_ack, 0); semaphore_initialize(new_socket->send_receive_mutex, 1); new_socket->socket_state = WAITING_SYNACK; minisocket_error s_error; int wait_val = 100; interrupt_level_t old_level; while (wait_val <= 12800) { send_control_message(MSG_SYN, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 0, 0, &s_error); new_socket->seq_number = 1; new_socket->ack_number = 0; if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } alarm_id a = register_alarm(wait_val, (alarm_handler_t) semaphore_V, new_socket->data_ready); semaphore_P(new_socket->data_ready); old_level = set_interrupt_level(DISABLED); if (queue_length(new_socket->data)) { deregister_alarm(a); } set_interrupt_level(old_level); if (!queue_length(new_socket->data)) { wait_val *= 2; continue; } network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; network_address_t saddr; unpack_address(header->source_address, saddr); int sport = unpack_unsigned_short(header->source_port); if (sport == new_socket->remote_port && network_compare_network_addresses(saddr, new_socket->remote_addr)) { if (header->message_type - '0' == MSG_SYNACK) { new_socket->seq_number = 1; new_socket->ack_number = 1; send_control_message(MSG_ACK, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 1, 1, &s_error); if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } network_interrupt_arg_t *packet = NULL; while (queue_dequeue(new_socket->data, (void **)&packet) != -1) { free(packet); } semaphore_initialize(new_socket->data_ready, 0); new_socket->socket_state = OPEN; new_socket->seq_number = 2; new_socket->ack_number = 1; return new_socket; } if (header->message_type -'0' == MSG_FIN) { minisocket_free(new_socket); return NULL; } } free(arg); } minisocket_free(new_socket); return NULL; }
minisocket_t* minisocket_server_create(int port, minisocket_error *error) { //Check socket number first if (valid_server_port(port) == 0) { *error = SOCKET_INVALIDPARAMS; return NULL; } //check if socket is already in use if (mini_socket_data[port] != NULL) { *error = SOCKET_PORTINUSE; return NULL; } minisocket_t* socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (socket == NULL) { *error = SOCKET_OUTOFMEMORY; return NULL; } socket->state = LISTENING; socket->socket_type = SERVER_TYPE; socket->local_port_number = port; network_address_t my_address; network_get_my_address(my_address); network_address_copy(my_address, socket->local_address); socket->datagrams_ready = semaphore_create(); semaphore_initialize(socket->datagrams_ready, 0); socket->ack_ready = semaphore_create(); semaphore_initialize(socket->ack_ready, 0); socket->incoming_data = queue_new(); socket->acknowledgements = queue_new(); socket->seq = 0; socket->ack = 0; socket->next_read = 0; //add socket to global array mini_socket_data[socket->local_port_number] = socket; char connection_established = 0; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_SYNACK); network_address_t dest; pack_unsigned_int(header.seq_number, socket->seq); pack_unsigned_int(header.ack_number, socket->ack); //loop until a full connection is established while (connection_established == 0) { //sleep until initial MSG_SYN arrives from client char message_received = 0; while (message_received == 0) { //Waits until it receives a packet semaphore_P(socket->ack_ready); interrupt_level_t old_level = set_interrupt_level(DISABLED); network_interrupt_arg_t* interrupt_message = NULL; queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); //TODO: check more contents of message? change seq and ack values in response??} if (interrupt_message != NULL ) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; unpack_address(received_header->source_address, dest); pack_address(header.destination_address, dest); pack_unsigned_short(header.destination_port, unpack_unsigned_short(received_header->source_port)); if (valid_client_port(unpack_unsigned_short(received_header->source_port)) == 1) { //check valid port number if (received_header->message_type != MSG_SYN) { //check valid message type header.message_type = MSG_FIN; network_send_pkt(dest, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); free(interrupt_message); } else { //set remote port values printf("server got the SYN message\n"); socket->remote_port_number = unpack_unsigned_short(received_header->source_port); network_address_copy(dest, socket->remote_address); socket->seq = unpack_unsigned_int(received_header->seq_number); message_received = 1; //will break loop free(interrupt_message); socket->state = CONNECTING; break; } } } } //reset loop value for return message_received = 0; //otherwise the message was the correct type and format int timeout = START_TIMEOUT / 2; //this way first timeout will be at START_TIMEOUT for (int i = 0; i < MAX_FAILURES; i++) { printf("sending MSG_SYNACK in server, i: %d\n", i); timeout = timeout * 2; header.message_type = MSG_SYNACK; network_send_pkt(dest, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!connection_established && !alarm_fired) { semaphore_P(socket->ack_ready); //alarm has fired, since there are no packets to be received if (queue_length(socket->acknowledgements) == 0) { alarm_fired = 1; //goes to next iteration of for loop } //There is a packet (alarm hasnt fired yet) else { network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // verify non null message if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK) { //same address, same ports, right message deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found connection_established = 1; //queue_prepend(socket->acknowledgements, interrupt_message); //ack works as well when there is data break; } else { free(interrupt_message); } } } } if (connection_established) { //correct response has been found, get out of this timeout loop! break; } } //if timeout occurs, will loop back to initial waiting phase for MSG_SYN from client } printf("leaving server create\n"); //update server socket values with client connection socket->state = CONNECTED; assert(socket != NULL); return socket; }
/* Transmit a packet and handle retransmissions */ int transmit_packet(minisocket_t socket, network_address_t dst_addr, int dst_port, short incr_seq, char message_type, int data_len, char* data, minisocket_error* error) { mini_header_reliable_t header; int alarm_id; short success = 0; network_address_t my_addr; int send_ret; interrupt_level_t prev_level; network_get_my_address(my_addr); // Return if the socket is invalid if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } // Don't allow new packets to be transmitted if the port's closing if (socket->status == TCP_PORT_CLOSING) { *error = SOCKET_SENDERROR; return -1; } // Increment the seq# if needed if (incr_seq == 1) { socket->seq_num++; } // Create the header header = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, message_type, socket->seq_num, socket->ack_num); // Can retransmit for each timeout up to 6400 (100 + 200 + 400 + ... + 6400 = 12.7s) socket->timeout = 100; while (socket->timeout <= 6400) { // send the packet send_ret = network_send_pkt(dst_addr, sizeof(struct mini_header_reliable), (char*) header, data_len, (char*) data); // Check if there was an error sending if (send_ret == -1) { // Could've been a fluke, let it keep going continue; } // Seq # is count of retransmissable packets... if dont increment it, // then this isn't retransmissable, so end if (incr_seq == 0) { free(header); return 0; } // Set an alarm to wake us up after timeout alarm_id = register_alarm(socket->timeout, &wake_up_sem, socket); // Specify the type of packet we're waiting for if (message_type == MSG_SYN) { socket->waiting = TCP_PORT_WAITING_SYNACK; } else { socket->waiting = TCP_PORT_WAITING_ACK; } // Sleep on semaphore prev_level = set_interrupt_level(DISABLED); semaphore_P(socket->wait_for_ack_sem); set_interrupt_level(prev_level); // Check if we got the packet when we've awoken if (socket->waiting == TCP_PORT_WAITING_NONE) { // Yes, we did - delete the alarm and break out of the loop deregister_alarm(alarm_id); success = 1; break; } else { if (socket->status == TCP_PORT_COULDNT_CONNECT) { // This means the client sent a FIN while we were trying to connect success = 0; deregister_alarm(alarm_id); break; } // No, we didn't - double the timeout and repeat if we can socket->timeout *= 2; } } // So the IH knows the sequence is done (see comment block in network_common:170) // There is a small chance that the code around network_common:170 won't catch // this in time and won't send one last packet - that is okay, as Prof. Sirer // mentioned sending that packet isn't even necessary in the first place. socket->timeout = 100; // Free the header as it's no longer needed free(header); // If we weren't successful... if (success == 0) { // Decrement the seq# if we incremented it earlier if (incr_seq == 1) { socket->seq_num--; } // Reset the socket's waiting status socket->waiting = TCP_PORT_WAITING_NONE; // Set the error & return *error = SOCKET_NOSERVER; return -1; } return 0; }
minisocket_t* minisocket_server_create(int port, minisocket_error *error) { if (port < MIN_SERVER_PORT || port > MAX_SERVER_PORT) { *error = SOCKET_INVALIDPARAMS; return NULL; } if (ports[port]) { *error = SOCKET_PORTINUSE; return NULL; } minisocket_t *new_socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (!new_socket) { *error = SOCKET_OUTOFMEMORY; return NULL; } // Initialize new socket new_socket->socket_state = INITIAL; semaphore_P(ports_mutex); ports[port] = new_socket; semaphore_V(ports_mutex); new_socket->socket_type = 's'; new_socket->local_port = port; network_address_copy(local_host, new_socket->local_addr); new_socket->data = queue_new(); new_socket->data_ready = semaphore_create(); new_socket->ack_flag = 0; new_socket->wait_for_ack = semaphore_create(); new_socket->send_receive_mutex = semaphore_create(); semaphore_initialize(new_socket->data_ready, 0); semaphore_initialize(new_socket->wait_for_ack, 0); semaphore_initialize(new_socket->send_receive_mutex, 1); interrupt_level_t old_level; while (1) { new_socket->seq_number = 0; new_socket->ack_number = 0; new_socket->remote_port = -1; new_socket->remote_addr[0] = 0; new_socket->remote_addr[1] = 0; new_socket->socket_state = WAITING_SYN; // receiving SYN while (1) { semaphore_P(new_socket->data_ready); network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; if (header->message_type -'0'== MSG_SYN) { unpack_address(header->source_address, new_socket->remote_addr); new_socket->remote_port = unpack_unsigned_short(header->source_port); new_socket->socket_state = WAITING_ACK; new_socket->seq_number = 0; new_socket->ack_number = 1; break; } else { free(arg); } } minisocket_error s_error; int wait_val = 100; while (wait_val <= 12800) { send_control_message(MSG_SYNACK, new_socket->remote_port, new_socket->remote_addr, new_socket->local_port, 0 , 1, &s_error); new_socket->seq_number = 1; new_socket->ack_number = 1; if (s_error == SOCKET_OUTOFMEMORY) { minisocket_free(new_socket); semaphore_P(ports_mutex); ports[new_socket->local_port] = NULL; semaphore_V(ports_mutex); *error = s_error; return NULL; } alarm_id a = register_alarm(wait_val, (alarm_handler_t) semaphore_V, new_socket->data_ready); semaphore_P(new_socket->data_ready); old_level = set_interrupt_level(DISABLED); if (queue_length(new_socket->data)) { deregister_alarm(a); } set_interrupt_level(old_level); if (!queue_length(new_socket->data)) { wait_val *= 2; continue; } network_interrupt_arg_t *arg = NULL; queue_dequeue(new_socket->data, (void **) &arg); mini_header_reliable_t *header = (mini_header_reliable_t *) arg->buffer; network_address_t saddr; unpack_address(header->source_address, saddr); int sport = unpack_unsigned_short(header->source_port); if (header->message_type - '0' == MSG_SYN) { if (new_socket->remote_port == sport && network_compare_network_addresses(new_socket->remote_addr, saddr)) { continue; } send_control_message(MSG_FIN, sport, saddr, new_socket->local_port, 0, 0, &s_error); } if (header->message_type - '0' == MSG_ACK) { if (new_socket->remote_port == sport && network_compare_network_addresses(new_socket->remote_addr, saddr)) { network_interrupt_arg_t *packet = NULL; while (queue_dequeue(new_socket->data, (void **)&packet) != -1) { free(packet); } semaphore_initialize(new_socket->data_ready, 0); new_socket->socket_state = OPEN; new_socket->seq_number = 1; new_socket->ack_number = 2; return new_socket; } } free (arg); } } return NULL; }
int minisocket_send(minisocket_t *socket, const char *msg, int len, minisocket_error *error) { if (socket->socket_state == CLOSED || socket->socket_state == CLOSING) { *error=SOCKET_SENDERROR; return 0; } if (!socket || socket->socket_state != OPEN || !msg || len == 0) { *error = SOCKET_INVALIDPARAMS; return -1; } semaphore_P(socket->send_receive_mutex); int fragment_length = MAX_NETWORK_PKT_SIZE - sizeof(mini_header_reliable_t); int transfer_length = len > fragment_length ? fragment_length : len; //int start_byte = 0; int sent_byte = 0; do { int wait = 100; mini_header_reliable_t *header = create_control_header(MSG_ACK, socket->remote_port, socket->remote_addr, socket->local_port, socket->seq_number, socket->ack_number); transfer_length = len - sent_byte > fragment_length ? fragment_length : len - sent_byte; while (wait <= 12800) { socket->ack_flag = 0; int res = network_send_pkt(socket->remote_addr, sizeof(mini_header_reliable_t), (char *)header, transfer_length, msg + sent_byte); socket->seq_number += transfer_length; if (res == -1) { *error = SOCKET_SENDERROR; semaphore_V(socket->send_receive_mutex); return (sent_byte == 0) ? -1 : sent_byte; } alarm_id a = register_alarm(wait, (alarm_handler_t) semaphore_V, socket->data_ready); semaphore_P(socket->wait_for_ack); if (socket->socket_state == CLOSED || socket->socket_state == CLOSING) { *error=SOCKET_SENDERROR; return 0; } interrupt_level_t old_level = set_interrupt_level(DISABLED); // Function was woken up by the firing of the alarm if (socket->ack_flag == 0) { wait *= 2; socket->seq_number -= transfer_length; semaphore_V(socket->send_receive_mutex); set_interrupt_level(old_level); continue; } // Function was woken up by the network handler else if (socket->ack_flag == 1) { // ACK has been received deregister_alarm(a); sent_byte += transfer_length; semaphore_V(socket->send_receive_mutex); set_interrupt_level(old_level); break; } } if (wait > 12800) { *error = SOCKET_SENDERROR; semaphore_V(socket->send_receive_mutex); return (sent_byte == 0) ? -1 : sent_byte; } } while (sent_byte != len); semaphore_V(socket->send_receive_mutex); return len; }
/* Close a connection. If minisocket_close is issued, any send or receive should * fail. As soon as the other side knows about the close, it should fail any * send or receive in progress. The minisocket is destroyed by minisocket_close * function. The function should never fail. */ void minisocket_close(minisocket_t socket) { interrupt_level_t l; minisocket_error error; resend_arg_t resend_alarm_arg; resend_alarm_arg = (resend_arg_t)calloc(1,sizeof(struct resend_arg)); if (!resend_alarm_arg){ return; } error = SOCKET_NOERROR; //printf("in minisocket_close\n"); //set state to close_wait //send close, wait using alarms //(after 7 retries, close connection) //P on ack_ready //if ack_ready, close connection //ill formed argument if (!sock_array){ free(resend_alarm_arg); return; } l = set_interrupt_level(DISABLED); //ill formed argument if (sock_array[socket->src_port] == NULL){ set_interrupt_level(l); free(resend_alarm_arg); return; } if (socket->curr_state == CLOSE_SEND || socket->curr_state == CLOSE_RCV){ set_interrupt_level(l); free(resend_alarm_arg); return; } socket->curr_state = CLOSE_SEND; socket->try_count = 0; socket->curr_seq++; minisocket_send_ctrl( MSG_FIN, socket, &error); //printf("in minisocket_close, sent my MSG_FIN\n"); resend_alarm_arg->sock = socket; resend_alarm_arg->msg_type = MSG_FIN; resend_alarm_arg->data_len = 0; resend_alarm_arg->data = (char*) &socket; //placeholder resend_alarm_arg->error = &error; if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); } socket->resend_alarm = NULL; socket->resend_alarm = set_alarm(RESEND_TIME_UNIT, minisocket_resend, resend_alarm_arg, minithread_time()); //printf("in minisocket_close, set my alarm.\n"); set_interrupt_level(l); semaphore_P(socket->ack_ready_sem); //wait for ack packet... //received ack, close connection //printf("in minisocket_close, got my ACK baby\n"); l = set_interrupt_level(DISABLED); if (socket->resend_alarm){ deregister_alarm(socket->resend_alarm); } socket->resend_alarm = NULL; minisocket_destroy(socket, &error); //printf("Closed. Error is %d", error); if (error != SOCKET_NOERROR){ //printf("Something went wrong. Close connection failure.\n"); } set_interrupt_level(l); //printf("in minisocket_close, SUCCESS\n"); free(resend_alarm_arg); return; }
minisocket_t* minisocket_client_create(const network_address_t addr, int port, minisocket_error *error) { //Check socket number first if (valid_server_port(port) == 0) { *error = SOCKET_INVALIDPARAMS; return NULL; } //Then check if we can make another client if (client_count >= MAX_CLIENTS) { *error = SOCKET_NOMOREPORTS; return NULL; } //create new minisocket minisocket_t* socket = (minisocket_t *) malloc(sizeof(minisocket_t)); if (socket == NULL) { *error = SOCKET_OUTOFMEMORY; return NULL; //OOM } while (mini_socket_data[next_port]) { next_port = ((next_port + 1) % PORT_RANGE); } socket->socket_type = CLIENT_TYPE; socket->local_port_number = next_port; next_port = ((next_port + 1) % PORT_RANGE); socket->state = CONNECTING; socket->seq = 0; socket->ack = 0; network_address_copy(addr, socket->remote_address); socket->remote_port_number = (unsigned short) port; network_address_copy(addr, socket->remote_address); socket->next_read = 0; network_address_t my_address; network_get_my_address(my_address); network_address_copy(my_address, socket->local_address); socket->datagrams_ready = semaphore_create(); semaphore_initialize(socket->datagrams_ready, 0); socket->ack_ready = semaphore_create(); semaphore_initialize(socket->ack_ready, 0); socket->incoming_data = queue_new(); socket->acknowledgements = queue_new(); //add socket to global array mini_socket_data[socket->local_port_number] = socket; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, addr, MSG_SYN); //minisocket and header now created, try to establish connection pack_unsigned_int(header.seq_number, socket->seq); pack_unsigned_int(header.ack_number, socket->ack); //send first message and wait for response int response = 0; //no response yet int timeout = START_TIMEOUT / 2; //this way first timeout will be at START_TIMEOUT for (int i = 0; i < MAX_FAILURES; i++) { printf("sending MSG_SYN in client, i: %d\n", i); timeout = timeout * 2; network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; while (!response && !alarm_fired) { semaphore_P(socket->ack_ready); //If queue length == 0, then the alarm must have fired if (queue_length(socket->acknowledgements) == 0) { alarm_fired = 1; //goes to next iteration of for loop } //otherwise, we received a packet else { network_interrupt_arg_t *interrupt_message; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); // if message not null if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0) { //if SYNACK printf("CLIENT\n"); if (received_header->message_type == MSG_SYNACK) { printf("GOT message SYN_ACK\n"); deregister_alarm(timeout_alarm_id); response = 1; break; } //if MSG_FIN else if (received_header->message_type == MSG_FIN) { printf("got message MSG_FIN in client\n"); //server already in use deregister_alarm(timeout_alarm_id); *error = SOCKET_BUSY; response = 1; minisocket_destroy(socket); return NULL; } //WRONG MESSAGE TYPE else { printf("wrong message type received in client\n"); //TODO : try another message type, maybe do nothing? } } } } } if (response) { break; } } // timeout after 12.8s occured, close down socket if (response != 1) { printf("full timeout in client sending SYN\n"); *error = SOCKET_NOSERVER; minisocket_destroy(socket); return NULL; } // send final MSG_ACK once to server (future data packets will also have MSG_ACK as header type) header.message_type = MSG_ACK; printf("sending final MSG_ACK and leaving client create\n"); network_send_pkt(addr, sizeof(mini_header_reliable_t), (char *) &header, 0, empty_message); socket->state = CONNECTED; client_count++; return socket; }
/* minisocket_process_packet is called within the network interrupt handler. * Therefore we can safely assume that interrupts are disabled. * */ void minisocket_process_packet(void* packet) { network_interrupt_arg_t* pkt; mini_header_reliable_t pkt_hdr; unsigned int seq_num; unsigned int ack_num; minisocket_error error; network_address_t src_addr; network_address_t dst_addr; unsigned int src_port; unsigned int dst_port; minisocket_t sock; int type; int data_len; pkt = (network_interrupt_arg_t*)packet; pkt_hdr = (mini_header_reliable_t)(&pkt->buffer); //printf("in minisocket_process_packet\n"); // error checking if (pkt->size < sizeof(struct mini_header_reliable)) { free(pkt); return; } unpack_address(pkt_hdr->source_address, src_addr); src_port = unpack_unsigned_short(pkt_hdr->source_port); dst_port = unpack_unsigned_short(pkt_hdr->destination_port); unpack_address(pkt_hdr->destination_address, dst_addr); seq_num = unpack_unsigned_int(pkt_hdr->seq_number); ack_num = unpack_unsigned_int(pkt_hdr->ack_number); data_len = pkt->size - sizeof(struct mini_header_reliable); if (src_port < 0 || dst_port < 0 || src_port >= NUM_SOCKETS || dst_port >= NUM_SOCKETS || !network_compare_network_addresses(dst_addr, my_addr)){ free(pkt); return; } error = SOCKET_NOERROR; sock = sock_array[dst_port]; if (sock == NULL) { free(pkt); return; } type = pkt_hdr->message_type; //printf("socket %d with state %d\n", sock->src_port, sock->curr_state); //printf("pkt ack number is %d\n", ack_num); //printf("pkt seq number is %d\n", seq_num); //printf("my ack number is %d\n", sock->curr_ack); //printf("my seq number is %d\n", sock->curr_seq); //printf("type of msg received is %d\n", type); if (sock->curr_state != LISTEN && ( !network_compare_network_addresses(src_addr, sock->dst_addr) || src_port != sock->dst_port ) ){ if (type == MSG_SYN){ minisocket_send_ctrl_to(MSG_FIN, sock, &error, src_addr, src_port); } free(pkt); return; } switch (sock->curr_state) { case LISTEN: if (type == MSG_SYN) { sock->curr_ack = 1; semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTING; sock->dst_port = src_port; network_address_copy(src_addr, sock->dst_addr); } free(pkt); break; case CONNECTING: if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { queue_append(sock->pkt_q, pkt); semaphore_V(sock->pkt_ready_sem); minisocket_send_ctrl(MSG_ACK, sock, &error); sock->curr_ack++; } else { free(pkt); } } else { free(pkt); } break; case CONNECT_WAIT://TODO if (type == MSG_FIN) { sock->curr_state = CLOSE_RCV; semaphore_V(sock->ack_ready_sem);//notify blocked guy } else if (type == MSG_SYNACK) { if (ack_num == sock->curr_seq) { sock->curr_state = CONNECTED; sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->ack_ready_sem); } } free(pkt); break; case MSG_WAIT: if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); semaphore_V(sock->ack_ready_sem);//notify blocked guy } else if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { //printf("got an ACK!\n"); semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { //printf("got a MESSAGE!\n"); queue_append(sock->pkt_q, pkt); sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->pkt_ready_sem); } else { free(pkt); } } else { free(pkt); } break; case CLOSE_SEND: if (type == MSG_ACK && ack_num == sock->curr_seq) { semaphore_V(sock->ack_ready_sem); } if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); //minisocket_send_ctrl(MSG_ACK, sock, &error); } //free(pkt); break; case CLOSE_RCV: if (type == MSG_FIN && ack_num == sock->curr_seq) { minisocket_send_ctrl(MSG_ACK, sock, &error); //semaphore_V(sock->ack_ready_sem); } free(pkt); break; case CONNECTED: if (type == MSG_FIN){ sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error);//notify to closer sock->curr_state = CLOSE_RCV; if (sock->resend_alarm){ deregister_alarm(sock->resend_alarm); sock->resend_alarm = NULL; } sock->resend_alarm = set_alarm(RESEND_TIME_UNIT * 150, self_destruct, (void*)sock, minithread_time()); } if (type == MSG_ACK) { if (ack_num == sock->curr_seq) { //semaphore_V(sock->ack_ready_sem); sock->curr_state = CONNECTED; } if (seq_num == sock->curr_ack+1 && data_len > 0) { //printf("got some DATA\n"); queue_append(sock->pkt_q, pkt); sock->curr_ack++; minisocket_send_ctrl(MSG_ACK, sock, &error); semaphore_V(sock->pkt_ready_sem); //printf("got data, no seg fault\n"); } else if (seq_num == sock->curr_ack && data_len > 0){ minisocket_send_ctrl(MSG_ACK, sock, &error); } else { free(pkt); } } else { free(pkt); } break; case EXIT: default: free(pkt); break; } }
int minisocket_send(minisocket_t *socket, const char *msg, int len, minisocket_error *error) { //Check params if (socket == NULL || mini_socket_data[socket->local_port_number] != socket || len < 0 || msg == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->state == CLOSED) { *error = SOCKET_SENDERROR; return -1; } if (len == 0) { return 0; } //can't send more bytes than we have if (len > strlen(msg)) { len = strlen(msg); } int bytes_sent = 0; int max_data_size = MAX_NETWORK_PKT_SIZE - sizeof(mini_header_reliable_t); int data_left = len; int packet_size; mini_header_reliable_t header; minisocket_create_reliable_header((mini_header_reliable_t *) &header, socket, socket->remote_port_number, socket->remote_address, MSG_ACK); //Keep sending until all is sent while (data_left != 0) { if (data_left >= max_data_size) { packet_size = max_data_size; } else { packet_size = data_left; } data_left -= packet_size; pack_unsigned_int(header.seq_number, socket->seq); int send_success = 0; //Attempt to send message int timeout = START_TIMEOUT/2; for (int i = 0; i < MAX_FAILURES; i++) { //printf("i: %d\n", i); timeout = timeout * 2; //update the seq and ack numbers for the header from the socket //printf("seq: %u, ack: %u\n", socket->seq, socket->ack); pack_unsigned_int(header.ack_number, socket->ack); pack_unsigned_int(header.seq_number, socket->seq); network_send_pkt(socket->remote_address, sizeof(mini_header_reliable_t), (char *) &header, packet_size, (char *) msg); alarm_id timeout_alarm_id = register_alarm(timeout, handshake_timeout_handler, (void *) socket->ack_ready); int alarm_fired = 0; //keep looking through received packets until either the alarm fires or it finds the correct packet while (!send_success && !alarm_fired) { semaphore_P(socket->ack_ready); if (queue_length(socket->acknowledgements) == 0) { // printf("no message in queue\n"); alarm_fired = 1; //goes to next iteration of for loop } else { // printf("length of queue currently %d\n", queue_length(socket->acknowledgements)); network_interrupt_arg_t* interrupt_message = NULL; interrupt_level_t old_level = set_interrupt_level(DISABLED); queue_dequeue(socket->acknowledgements, (void **) &interrupt_message); set_interrupt_level(old_level); if (interrupt_message != NULL) { mini_header_reliable_t* received_header = (mini_header_reliable_t *) interrupt_message->buffer; network_address_t temp_address; unpack_address(received_header->source_address, temp_address); unsigned int new_ack = unpack_unsigned_int(received_header->ack_number); //printf("Received an acknowledgment with ack number %u \n", new_ack); if (socket->remote_port_number == unpack_unsigned_short(received_header->source_port) && network_compare_network_addresses(socket->remote_address, temp_address) != 0 && received_header->message_type == MSG_ACK && new_ack == (socket->seq + packet_size)) { //same address, same ports, right message, right ack deregister_alarm(timeout_alarm_id); //only deregister alarm when the right packet is found send_success = 1; bytes_sent += packet_size; msg += packet_size; socket->seq += packet_size; free(interrupt_message); break; } else { free(interrupt_message); } } } } if (send_success) { break; } } if (!send_success) { //Got a timeout, should close down socket *error = SOCKET_SENDERROR; minisocket_destroy(socket); return bytes_sent; } } //printf("\n\n"); return bytes_sent; }
//Transmit a packet and handle retransmission attempts int transmit_packet(minisocket_t socket, network_address_t dst_addr, int dst_port, short incr_seq, char message_type, int data_len, char* data, minisocket_error* error) { mini_header_reliable_t newReliableHeader; void *alarmId; int sendSucessful; network_address_t my_addr; int success = 0; int connected = 0; if (message_type == MSG_ACK) connected = 1; network_get_my_address(my_addr); if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (socket->status == TCP_PORT_CLOSING) { *error = SOCKET_SENDERROR; return -1; } newReliableHeader = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, message_type, socket->seq_number, socket->ack_number); socket->timeout = 100; while(socket->timeout <= 6400) { printf("sending packet to %d, seq=%d, ack=%d, type=%d\n", dst_port, socket->seq_number, socket->ack_number, message_type); sendSucessful = network_send_pkt(dst_addr, sizeof(struct mini_header_reliable), (char*) newReliableHeader, data_len, (char*) data); if (sendSucessful == -1) { socket->timeout *= 2; continue; } alarmId = register_alarm(socket->timeout, &wake_up_semaphore, socket); if (message_type == MSG_SYN) { socket->waiting = TCP_PORT_WAITING_SYNACK; semaphore_P(socket->wait_for_ack_semaphore); } else if (!connected) { socket->waiting = TCP_PORT_WAITING_ACK; semaphore_P(socket->wait_for_ack_semaphore); } else { socket->waiting = TCP_PORT_WAITING_NONE; } semaphore_P(socket->mutex); if (socket->waiting == TCP_PORT_WAITING_NONE) { /* I think incr_seq belongs before the _P on wait_for_ack, but that breaks it for now :/ if (incr_seq) { semaphore_P(socket->mutex); socket->seq_number++; semaphore_V(socket->mutex); } */ if (message_type == MSG_SYN) { //we got a synack back, so we need to make sure to send an ack to the server newReliableHeader = create_reliable_header(my_addr, socket->port_number, dst_addr, dst_port, MSG_ACK, socket->seq_number, socket->ack_number); socket->timeout = 100; message_type = MSG_ACK; connected = 1; semaphore_V(socket->mutex); continue; } deregister_alarm(alarmId); success = 1; semaphore_V(socket->mutex); break; } else { if (socket->status == TCP_PORT_UNABLE_TO_CONNECT) { deregister_alarm(alarmId); success = 0; semaphore_V(socket->mutex); break; } socket->timeout *= 2; semaphore_V(socket->mutex); } } semaphore_P(socket->mutex); socket->timeout = 100; if (success == 0) { socket->waiting = TCP_PORT_WAITING_NONE; *error = SOCKET_NOSERVER; } semaphore_V(socket->mutex); *error = SOCKET_NOERROR; free(newReliableHeader); return 0; }
/* sends a miniroute packet, automatically discovering the path if necessary. See description in the * .h file. */ int miniroute_send_pkt(network_address_t dest_address, int hdr_len, char* hdr, int data_len, char* data) { // This will store the route request struct, which is a structure related to the // search for a path to the host route_request_t route_request; // Store the routing header routing_header_t routing_header; // Store the route to the host, which is an array of addresses network_address_t* route; // Store the route data struct, which holds the route and some metadata route_data_t route_data; // Store my address network_address_t my_addr; // Used to synchronize access with structures the network handler touches interrupt_level_t prev_level; // This will store the combined routing + normal headers char* full_header; network_address_t dest_address2; // These will store data related to the routes int time_route_found; int route_len; int route_valid = 1; // Used to get data from the header containing the paht routing_header_t tmp_routing_header; // Used to just check the IP of senders; combats UDP port issues w/ simulated broadcasts unsigned int dest_address_ip = dest_address[0]; // Loop + tmp variables int current_req_id; int success = 0; int alarm_id; int x; int i; if (hdr_len == 0 || hdr == NULL || data_len == 0 || data == NULL) return -1; // Get the route item, which is a hashmap_item_t, from the hashmap for this addr semaphore_P(route_cache_sem); route_data = (route_data_t) hashmap_get(route_cache, hash_address(dest_address)); // If it's not NULL, extract the data from the item if (route_data != NULL) { time_route_found = route_data->time_found; route_len = route_data->route_len; // caveat: the cleanup thread may delete the route data, so we need to // save it in a separate variable, just incase. route = (network_address_t*) malloc(sizeof(network_address_t) * route_len); if (route == NULL) { semaphore_V(route_cache_sem); return -1; } memcpy(route, route_data->route, sizeof(network_address_t) * route_len); } else { route_valid = 0; } semaphore_V(route_cache_sem); // Check, if the route isn't NULL, if it's expired if (route_valid == 1 && (ticks - time_route_found) * PERIOD/MILLISECOND > 3000) { route_valid = 0; } // If the route is invalid (either not in the cache or expired)... if (route_valid == 0) { // We won't be needing that previous route variable if (route_data != NULL) { // But, just in case someone is still using it, use the route cache semaphore semaphore_P(route_cache_sem); free(route); semaphore_V(route_cache_sem); } // Check if someone else already initiated this route discovery request prev_level = set_interrupt_level(DISABLED); route_request = (route_request_t) hashmap_get(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // If so, we can just wait for their result if (route_request != NULL) { // Wait for the other thread to get the path // The threads waiting variable needs to be synchronized. We decided // to reuse the route cache sem, as there will not be much lock // contention semaphore_P(route_cache_sem); route_request->threads_waiting++; semaphore_V(route_cache_sem); semaphore_P(route_request->waiting_sem); // Get the route from the hashmap semaphore_P(route_cache_sem); route_data = (route_data_t) hashmap_get(route_cache, hash_address(dest_address)); // If the other thread didn't get the route, return an error if (route_data == NULL) { // Return failure... semaphore_V(route_cache_sem); return -1; } else { time_route_found = route_data->time_found; route_len = route_data->route_len; if ((ticks - time_route_found) * PERIOD/MILLISECOND > 3000) { // This could have been a left-over expired cache entry that we haven't // deleted yet. semaphore_V(route_cache_sem); return -1; } // Save the route in a separate variable in case the route gets cleaned up // while we're using it route = (network_address_t*) malloc(sizeof(network_address_t) * route_len); if (route == NULL) { semaphore_V(route_cache_sem); return -1; } memcpy(route, route_data->route, sizeof(network_address_t) * route_len); semaphore_V(route_cache_sem); } } else { // Otherwise, we have to do the route discovery process // Create a new route request struct route_request = create_route_request(); if (route_request == NULL) { return -1; } // Add the route request to the current discovery requests prev_level = set_interrupt_level(DISABLED); hashmap_insert(current_discovery_requests, dest_address_ip, route_request); set_interrupt_level(prev_level); // We'll try the route discovery process three times for (i = 0; i < 3; i++) { // Register an alarm to wake this thread up as it waits for a response alarm_id = register_alarm(12000, &alarm_wakeup_sem, (void*) route_request->initiator_sem); // Increment the request ID - must be synchronized, obviously semaphore_P(request_id_sem); current_req_id = route_request_id++; semaphore_V(request_id_sem); // We need to make a header for the discovery request, but the path // needs to have our address in it, so the reply can be forwarded back // to us network_get_my_address(my_addr); // Passing in the address of this local variable will suffice, as the // value is immediately copied into the header and then not used again // Create a routing header for the route discovery request routing_header = create_miniroute_header(ROUTING_ROUTE_DISCOVERY, dest_address, current_req_id, MAX_ROUTE_LENGTH, 1, &my_addr); if (routing_header == NULL) { return -1; } // Combine it with the given header full_header = merge_headers(routing_header, hdr, hdr_len); if (full_header == NULL) { free(routing_header); return -1; } // Send out the route discovery request network_bcast_pkt(sizeof(struct routing_header)+hdr_len, (char*) full_header, data_len, data); // Wait for a reply (which will be signalled by the network handler) prev_level = set_interrupt_level(DISABLED); semaphore_P(route_request->initiator_sem); set_interrupt_level(prev_level); // Check if we got a successful response if (route_request->interrupt_arg != NULL) { // Deregister the alarm before it tries to wake us up // Needs to be synchronized, as the IH touches it and we destroy it here prev_level = set_interrupt_level(alarm_id); deregister_alarm(alarm_id); set_interrupt_level(alarm_id); // Get the header tmp_routing_header = (routing_header_t) route_request->interrupt_arg->buffer; route_len = unpack_unsigned_int(tmp_routing_header->path_len); // Then the path, for our own use later in this function // We'll also create one copy and put it in the route data struct route = miniroute_reverse_raw_path(tmp_routing_header, route_len); if (route == NULL) { free(routing_header); free(full_header); return -1; } // Create a route data struct - with a different route (as it will be deleted by a diff thread) route_data = create_route_data(miniroute_reverse_raw_path(tmp_routing_header, route_len), route_len, ticks); if (route_data == NULL) { free(routing_header); free(full_header); return -1; } // add it to the cache hashmap semaphore_P(route_cache_sem); hashmap_insert(route_cache, hash_address(dest_address), route_data); semaphore_V(route_cache_sem); // Wake up the other threads waiting for (x = 0; x < route_request->threads_waiting; x++) { semaphore_V(route_request->waiting_sem); } // Clean up the route request struct, then delete it from the hashmap // DELETE ROUTE REQUEST WILL FREE THE NETWORK INTERRUPT ARG! prev_level = set_interrupt_level(DISABLED); delete_route_request(route_request); hashmap_delete(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // We don't need to actually get any of the routing stuff from the // route_ite, as this process also sent the data packet // Free the headers free(routing_header); free(full_header); // Return the total bytes sent, not including the routing header success = 1; break; } } // If we didn't get a successful response after 3 tries... if (success == 0) { // Wake up the other threads waiting so they can see we failed for (x = 0; x < route_request->threads_waiting; x++) { semaphore_V(route_request->waiting_sem); } // clean up the route request struct, then delete it from the hashmap prev_level = set_interrupt_level(DISABLED); delete_route_request(route_request); hashmap_delete(current_discovery_requests, dest_address_ip); set_interrupt_level(prev_level); // Free the headers free(routing_header); free(full_header); // Return failure... return -1; } } } // If we're here, we either found the route in the cache or waited for another // thread to finish getting the route (and it did so successfully) network_address_copy(route[route_len-1], dest_address2); // Need to update the dst address to deal with UDP port issues // This again is due to UDP port issues... pack_address(((mini_header_t) hdr)->destination_address, dest_address2); // Create a routing header for the data packet routing_header = create_miniroute_header(ROUTING_DATA, dest_address2, 0, MAX_ROUTE_LENGTH, route_len, route); if (routing_header == NULL) { return -1; } // Combine it with the given header full_header = merge_headers(routing_header, hdr, hdr_len); if (full_header == NULL) { free(routing_header); } // Set the right destination address network_address_copy(route[1], dest_address2); // Send the packet network_send_pkt(dest_address2, sizeof(struct routing_header) + hdr_len, full_header, data_len, data); // Free the route + headers free(route); free(routing_header); free(full_header); // Return the total data sent return hdr_len + data_len; }