static void raw_send(const char *str, int payload_len) { uint16_t c; int len = 8 /* udp header */ + payload_len; /* IPv6 header → length */ uip_buf[19] = len; /* UDP header → length */ uip_buf[59] = len; /* initialize UDP checksum to zero */ uip_buf[60] = 0x00; uip_buf[61] = 0x00; for (c = 0; c < payload_len; c++) uip_buf[62 + c] = str[c]; /* calculate UDP checksum */ uint16_t sum = 0; sum = len + 17; sum = chksum(sum, (uint8_t*)&uip_buf[22], 2 * 16); sum = chksum(sum, &uip_buf[54], len); sum = (sum == 0 ? 0xffff : ~sum); if (sum == 0) sum = 0xffff; uip_buf[60] = (sum & 0xFF00) >> 8; uip_buf[61] = (sum & 0x00FF); uip_len = 62 + payload_len; transmit_packet(); }
/* * this function is always called with interrupts off * this function also assumes that there is space to send in the Emaclite buffer */ static err_t _unbuffered_low_level_output(XEmacLite *instancep, struct pbuf *p) { struct pbuf *q; int total_len = 0; #if ETH_PAD_SIZE pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */ #endif for(q = p, total_len = 0; q != NULL; q = q->next) { /* Send the data from the pbuf to the interface, one pbuf at a time. The size of the data in each pbuf is kept in the ->len variable. */ memcpy(xemac_tx_frame + total_len, q->payload, q->len); total_len += q->len; } if (transmit_packet(instancep, xemac_tx_frame, total_len) < 0) { #if LINK_STATS lwip_stats.link.drop++; #endif } #if ETH_PAD_SIZE pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ #endif #if LINK_STATS lwip_stats.link.xmit++; #endif /* LINK_STATS */ return ERR_OK; }
/* Spoof Limit Checking: There is a known issue with the Vsense values, but we should at least get the framework set up for limit checking on arbitrary values. The power board should have upper and lower limits for vsense and csense data, and turn off components if their voltage/current is too high. ONLY CHECKS BATTERY 1 VOLTAGE LINE RIGHT NOW. */ void limit_check( void ) { unsigned char sw; SVIT_t *component; // turn off all switches and send ack_command w/ value of SAFE_MODE if (percent < SAFE_MODE) { safe_mode = 1; for (sw = 0; sw < sizeof(components); sw++) { component = &svit[components[sw]]; switch_off( component->switch_num ); component->switch_state = SW_OFF; } component = &svit[TORQUER_1]; component->switch_state = SW_OFF; component = &svit[TORQUER_2]; component->switch_state = SW_OFF; component = &svit[TORQUER_3]; component->switch_state = SW_OFF; torquer_off(TORQUER_1); torquer_off(TORQUER_2); torquer_off(TORQUER_3); // Only transmit once if (!transmit_safe && !been_to_safe) { transmit_packet( 0, VCP_ACK, SAFE_MODE); transmit_safe = 1; } else {} been_to_safe = 1; transmit_safe = 1; } else if (percent > SHUNT_MODE) { safe_mode = 0; // turn on the maestro and send ack_command w/ value of SHUNT_MODE component = &svit[MAESTRO]; switch_on( component->switch_num ); component->switch_state = SW_ON; // Only transmit once if (!transmit_shunt && !been_to_shunt) { transmit_packet( 0, VCP_ACK, SHUNT_MODE); } else {} been_to_shunt = 1; transmit_shunt = 1; } else {} // To avoid annoying compile warning }
//Destroys minisockets void minisocket_destroy(minisocket_t minisocket, int FIN) { int portNumber; int i, threads; interrupt_level_t prev_level; minisocket_error error; if (minisocket == NULL) return; portNumber = minisocket->port_number; semaphore_P(destroy_semaphore); minisocket->waiting = TCP_PORT_WAITING_TO_CLOSE; if (minisockets[portNumber] == NULL) return; semaphore_V(minisocket->packet_ready); semaphore_P(minisocket->mutex); if (minisockets[portNumber] == NULL) return; if (FIN == 1) { transmit_packet(minisocket, minisocket->destination_addr, minisocket->destination_port, 1, MSG_FIN, 0, NULL, &error); } minisocket->status = TCP_PORT_CLOSING; prev_level = set_interrupt_level(DISABLED); threads = minisocket->num_waiting_on_mutex; for (i = 0; i < threads; i++) { semaphore_V(minisocket->mutex); i++; } set_interrupt_level(prev_level); minisockets[portNumber] = NULL; semaphore_destroy(minisocket->wait_for_ack_semaphore); semaphore_destroy(minisocket->mutex); semaphore_destroy(minisocket->packet_ready); if (minisocket->data_length != 0) free(minisocket->data_buffer); queue_free(minisocket->waiting_packets); free(minisocket); semaphore_V(destroy_semaphore); }
/*---------------------------------------------------------------------------*/ static int radio_send(const void *payload, unsigned short payload_len) { prepare_packet(payload, payload_len); transmit_packet(payload_len); return RADIO_TX_OK; }
void test_transmission() { kprintf("Testing transmission...\n"); uint8_t data[50]; int i, j; for (i = 0; i < 50; i++) { data[i] = i; } for (i = 0; i < 18; i++) { kprintf("buf %x size %x\n", data, 50); transmit_packet(data, 50); for (j = 0; j < 50; j++) data[j] += 50; } }
/* * Do the actual transmission of the packet. The packet is contained * in the pbuf that is passed to the function. This pbuf might be chained. * Return 1 when the packet is succesfully queued for transmission. * Or return 0 if the packet is lost. */ static bool_t eth_output(eth_t *u, buf_t *p, small_uint_t prio) { mutex_lock(&u->netif.lock); /* debug_printf("From tcp out\n"); */ /* Exit if link has failed */ if (p->tot_len < 4 || p->tot_len > ETH_MTU /*|| ! (phy_read (u, PHY_STS) & PHY_STS_LINK)*/) { ++u->netif.out_errors; mutex_unlock(&u->netif.lock); /*debug_printf ("output: transmit %d bytes, link failed\n", p->tot_len);*/ netif_free_buf (&u->netif, p); return 0; } /*debug_printf ("output: transmit %d bytes\n", p->tot_len);*/ if (ARM_ETH->STAT & ARM_ETH_X_EMPTY) { /* Смело отсылаем. */ transmit_packet(u, p); mutex_unlock(&u->netif.lock); netif_free_buf (&u->netif, p); return 1; } /* Занято, ставим в очередь. */ /*if (buf_queue_is_full(&u->outq)) { // Нет места в очереди: теряем пакет. ++u->netif.out_discards; ++u->out_full_buf; mutex_unlock(&u->netif.lock); buf_free(p); return 0; }*/ while (buf_queue_is_full(&u->outq)) asm volatile ("nop;"); buf_queue_put(&u->outq, p); mutex_unlock(&u->netif.lock); return 1; }
/* * Kick the watchdog. * This is done by sending a heartbeat message to the board controller watchdog * through drbcc-core. */ static int wd_keepalive(void) { int ret; struct bcc_packet pkt = { .cmd = DRBCC_REQ_HEARTBEAT, .payloadlen = 2, }; pkt.data[0] = timeout >> 8; pkt.data[1] = timeout; DBGF(BWD "Send timeout to board controller (current timeout: %d).", timeout); if ((ret = transmit_packet(&pkt)) < 0) { ERR(BWD "Error while trying to send heartbeat to board controller."); return -EFAULT; } if(pkt.cmd != cmd_responses[DRBCC_REQ_HEARTBEAT]) { if(pkt.cmd == DRBCC_TIMEOUT) { DBG("Waiting for ACK for HEARTBEAT message timed out."); } else { DBGF("Received message with wrong response type: %x", pkt.cmd); } return -EFAULT; } return ret; } static void blocking_wd_keepalive_thread(struct work_struct *work) { wd_keepalive(); queue_delayed_work(timeout_keepalive_wq, &tm_work, WD_KEEPALIVE_TIME*HZ); }
void network_handler(network_interrupt_arg_t* arg) { // Used to extract the network address out of the interrupt argument //network_address_t addr; // The source port number - this is really the remote unbound port number int src_port_number; // The port number the packet was sent to int dst_port_number; // Used to store the header data for UDP/minimsgs mini_header_t header; // This is used to extract the sender's address from the header network_address_t src_addr; // This is used to extract the destination (our) address from the header network_address_t dst_addr; // Disable interrupts... interrupt_level_t prev_level = set_interrupt_level(DISABLED); // This is used to check our own address for sanity checks network_address_t my_addr; // This will store the total packet size int packet_size; // This will store the size of the data in the packet int data_len; // This will store the header of a TCP packet mini_header_reliable_t header_reliable; // This will store the ACK number of a TCP packet int ack_num; // This will store the Sequence number of a TCP packet int seq_num; // This will store the socket minisocket_t socket; // This will store the TCP error minisocket_error error; // This will be used to indicate whether the TCP packet is a duplicate or not int duplicate = 0; // This will tell us if we need to free the arg or not int enqueued; // Used for general for loops int i; // Used to check if we've already broadcasted a route discovery req int* last_seen_req_id; // Used for various tasks network_address_t tmp_addr; network_address_t tmp_addr2; // Used to handle routing and discovery requests route_request_t route_request; int current_req_id; int path_len; int ttl; // Get the buffer without the routing header char* buffer_without_routing = (char*) (arg->buffer + sizeof(struct routing_header)); // Used to get the data buffer char* data_buffer; // Handle the mini route stuff // Extract the information from the routing header routing_header_t routing_header = (routing_header_t) arg->buffer; char routing_packet_type = routing_header->routing_packet_type; unpack_address(routing_header->destination, dst_addr); current_req_id = unpack_unsigned_int(routing_header->id); ttl = unpack_unsigned_int(routing_header->ttl); path_len = unpack_unsigned_int(routing_header->path_len); // Get the data buffer & data length switch(buffer_without_routing[0]) { case (char) PROTOCOL_MINISTREAM: data_buffer = (char*) (buffer_without_routing + sizeof(struct mini_header_reliable)); data_len = arg->size - sizeof(struct routing_header) - sizeof(struct mini_header_reliable); break; //default: todo: put this back in, but leave w/o it for testing case (char) PROTOCOL_MINIDATAGRAM: data_buffer = (char*) (buffer_without_routing + sizeof(struct mini_header)); data_len = arg->size - sizeof(struct routing_header) - sizeof(struct mini_header); break; } network_get_my_address(my_addr); //if (network_compare_network_addresses(my_addr, dst_addr) == 0 && ttl == 0) if (my_addr[0] != dst_addr[0] && ttl == 0) { free(arg); set_interrupt_level(prev_level); return; } switch (routing_packet_type) { // If this is a data packet case ROUTING_DATA: // If the data packet is meant for us, then break and let the higher // protocols get the data unpack_address(routing_header->path[path_len-1], tmp_addr); if (network_compare_network_addresses(my_addr, tmp_addr) != 0) { break; } else { // If it's not meant for us, we must pass it along // Go through the path and find the next node for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); // If this node is us, break - the node we need to send to is next if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we're the last node (i == path_len-1) or we weren't found in it, quit if (i >= path_len - 1) { free(arg); set_interrupt_level(prev_level); return; } // Now we'll forward the packet by reusing the headers we have // Get the next host in the path and set it as the packet's dst unpack_address(routing_header->path[i+1], tmp_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); // Send the packet onward in the route network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, data_len, data_buffer); // Revert the header back (not entirely necessary) //pack_unsigned_int(routing_header->ttl, ttl); } free(arg); set_interrupt_level(prev_level); return; break; case ROUTING_ROUTE_DISCOVERY: // We're not the dst, so just forward this packet along //if (network_compare_network_addresses(my_addr, dst_addr) == 0) if (my_addr[0] != dst_addr[0]) { // Check if we're in the path, if so, no need to send again (no loops) for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we were in the path, return - no need to do anything else if (i < path_len) { free(arg); set_interrupt_level(prev_level); return; } // todo: this next part w/ the discovery packets seen is probably wrong... // also, lookup the "explain" part in my txt // todo: need something to clean up old discovery packets // Check if we've already seen this discovery request - if so, don't resend last_seen_req_id = (int*) hashmap_get(discovery_packets_seen, current_req_id); if (last_seen_req_id != NULL) { // If this exists, then we've already seen this packet - no need to resend free(arg); set_interrupt_level(prev_level); return; } // Now we'll rebroadcast the discovery packet by reusing the header we have // Modify the header as needed pack_unsigned_int(routing_header->path_len, path_len+1); pack_address(routing_header->path[path_len], my_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); // Broadcast this packet network_bcast_pkt(arg->size - data_len, (char*) arg->buffer, data_len, data_buffer); // Revert the header - dont need to actually remove from route /*pack_unsigned_int(routing_header->path_len, path_len); pack_unsigned_int(routing_header->ttl, ttl); // update already broadcasted hashmap - just put a garbage ptr in hashmap_insert(discovery_packets_seen, current_req_id, (void*) 0x555555); */ free(arg); set_interrupt_level(prev_level); return; } else { // If we were the host being sought, send a reply packet back // and ensure the higher protocols get the data sent // reverse the path so we can send a packet back to the host // Don't forget to add ourselves to the route path_len++; pack_unsigned_int(routing_header->path_len, path_len); pack_address(routing_header->path[path_len-1], my_addr); for (i = 0; i < path_len/2; i++) { unpack_address(routing_header->path[path_len-1-i], tmp_addr); unpack_address(routing_header->path[i], tmp_addr2); pack_address(routing_header->path[i], tmp_addr); pack_address(routing_header->path[path_len-1-i], tmp_addr2); } // We'll start sending the packet back now by reusing the header we have // Prepare the headers pack_unsigned_int(routing_header->ttl, MAX_ROUTE_LENGTH); routing_header->routing_packet_type = ROUTING_ROUTE_REPLY; // send a route reply packet, starting from the next host in the reversed route unpack_address(routing_header->path[1], tmp_addr); pack_address(routing_header->destination, tmp_addr); network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, 0, NULL); // Revert the header /*pack_unsigned_int(routing_header->ttl, ttl); routing_header->routing_packet_type = ROUTING_ROUTE_DISCOVERY; // Reverse the path back for (i = 0; i < path_len/2; i++) { unpack_address(routing_header->path[path_len-1-i], tmp_addr); unpack_address(routing_header->path[i], tmp_addr2); pack_address(routing_header->path[i], tmp_addr); pack_address(routing_header->path[path_len-1-i], tmp_addr); } */ // DONT return, ensure that the higher protocols will get the data // ^ never mind. return, it will send a data packet later. free(arg); set_interrupt_level(prev_level); return; } break; case ROUTING_ROUTE_REPLY: unpack_address(routing_header->path[path_len-1], tmp_addr); // If we were the initiator of the request and just got our response //if (network_compare_network_addresses(my_addr, tmp_addr) != 0) if (my_addr[0] == tmp_addr[0]) { // Get the addr of the host we were trying to discover unpack_address(routing_header->path[0], tmp_addr); // find the discovery request struct for this dst addr //route_request = (route_request_t) hashmap_get(current_discovery_requests, hash_address(tmp_addr)); route_request = (route_request_t) hashmap_get(current_discovery_requests, tmp_addr[0]); if (route_request == NULL) { free(arg); set_interrupt_level(prev_level); return; // it could be we already got this path break; } // Check if we already got this path, but miniroute_send_pkt() hasn't deleted the req struct yet if (route_request->interrupt_arg != NULL) { free(arg); set_interrupt_level(prev_level); return; break; } route_request->interrupt_arg = arg; semaphore_V(route_request->initiator_sem); set_interrupt_level(prev_level); return; } else { // Find the next node in the route for (i = 0; i < path_len; i++) { unpack_address(routing_header->path[i], tmp_addr); // Stop if we found ourselves - we need to send to the next node if (network_compare_network_addresses(tmp_addr, my_addr) != 0) { break; } } // If we were the last node OR not in the route at all if (i >= path_len - 1) { free(arg); set_interrupt_level(prev_level); return; } // We'll forward the reply along by reusing the header // Get the next host in the path and set it as the packet's dst unpack_address(routing_header->path[i+1], tmp_addr); pack_unsigned_int(routing_header->ttl, ttl - 1); pack_address(routing_header->destination, tmp_addr); // Send the packet onward in the route network_send_pkt(tmp_addr, arg->size - data_len, (char*) arg->buffer, 0, NULL); // Make sure we set the header back //pack_unsigned_int(routing_header->ttl, ttl); } free(arg); set_interrupt_level(prev_level); return; break; } // If we're here, the packet was meant for us // The "normal" packet without the routing header is in buffer_without_routing // Adjust arg->size in case something uses it arg->size -= sizeof(struct routing_header); // Check the protocol switch (buffer_without_routing[0]) { case (char) PROTOCOL_MINIDATAGRAM: // Extract data from the network interrupt arg //network_address_copy(arg->addr, addr); // Get the header struct, unpack the parameters header = (mini_header_t) buffer_without_routing; dst_port_number = (int) unpack_unsigned_short(header->destination_port); // Ensure the port number is valid if (dst_port_number < MIN_UNBOUND || dst_port_number > MAX_UNBOUND) { free(arg); set_interrupt_level(prev_level); return; } // Then ensure the miniport exists if (miniports[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Add the arg to the queue queue_append(miniports[dst_port_number]->port_data.unbound.data_queue, arg); // Wake up thread that's waiting to receive - sem_V() semaphore_V(miniports[dst_port_number]->port_data.unbound.data_ready); // Ensure the argument isn't free'd enqueued = 1; set_interrupt_level(prev_level); return; break; case PROTOCOL_MINISTREAM: // Get the total size of the packet and data length packet_size = arg->size; data_len = packet_size - sizeof(struct mini_header_reliable); // Get the header and extract information header_reliable = (mini_header_reliable_t) buffer_without_routing; src_port_number = (int) unpack_unsigned_short(header_reliable->source_port); dst_port_number = (int) unpack_unsigned_short(header_reliable->destination_port); unpack_address(header_reliable->source_address, src_addr); unpack_address(header_reliable->destination_address, dst_addr); seq_num = (int) unpack_unsigned_int(header_reliable->seq_number); ack_num = (int) unpack_unsigned_int(header_reliable->ack_number); // Don't respond if socket doesn't exist - will trigger SOCKET_NOSERVER for client if (minisockets[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Get the socket in question socket = minisockets[dst_port_number]; // Check if packet was meant for us - ignore if not network_get_my_address(my_addr); if (network_compare_network_addresses(my_addr, dst_addr) == 0) { free(arg); set_interrupt_level(prev_level); return; } if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE) { free(arg); set_interrupt_level(prev_level); return; } // Packet handling for established connections if (socket->status != TCP_PORT_LISTENING) { // Ensure source address is correct - ignore if not if (network_compare_network_addresses(src_addr, socket->dst_addr) == 0 || src_port_number != socket->dst_port) { if (header_reliable->message_type == MSG_SYN) { // A client is trying to connect to an already-connected port // Send them a FIN, which will result in their client // returning a SOCKET_BUSY error transmit_packet(socket, src_addr, src_port_number, 0, MSG_FIN, 0, NULL, &error); } free(arg); set_interrupt_level(prev_level); return; } /* We got a duplicate SYN and need to ensure that the host gets * another SYNACK. The thread that sent the SYNACK will currently * be retransmitting the SYNACKs, as it didn't get an ACK. If it * was done with the retransmission sequence, the socket would be * closed, and this would therefore not have gotten this far. * HOWEVER: * If the retransmission sequence already sent its LAST * retransmission and was waiting on it, then this duplicate SYN * will NOT get a SYNACK, as the retransmission is just waiting * out the last alarm before it ends. Therefore, if this is the case, * reset the timeout to the last timeout value so it sends just * one more SYNACK. * * Professor Sirer mentioned that this isn't even necessary, * but this does seem to make the code follow the specs more. */ if (header_reliable->message_type == MSG_SYN) { // If the timeout is at 6400, then it's the last transmission // but it hasn't been doubled yet. If it's 12800, it's just // been doubled. In each case we want to keep it at 6400 // at the end of the while loop, so we set the timeout to half // it's current value. if (socket->timeout >= 6400) { socket->timeout /= 2; } free(arg); set_interrupt_level(prev_level); return; } // If we were trying to connect and got a FIN, that means the socket's busy if (socket->status == TCP_PORT_CONNECTING && header_reliable->message_type == MSG_FIN) { // Not connected, got a FIN - that means we couldnt start connection b/c it was busy // This will let the client function infer that socket->status = TCP_PORT_COULDNT_CONNECT; // Wake it up so it can end the retransmission sequence semaphore_V(socket->wait_for_ack_sem); free(arg); set_interrupt_level(prev_level); return; } /* Note: the code below handles ACKs. It also inherently deals with * duplicate ACKs. We have a status code that indicates whether the socket * is waiting for an ACK or not. If it's set and we get an ACK (or any * packet) with the right ACK number, we can process it. However, if our * status indicates we're NOT waiting for an ACK, we can infer from the * fact that window_size = 1 that we already got the only ACK we could've * been expecting, and this new one is therefore a duplicate. */ // If we're waiting on an ACK if (socket->waiting == TCP_PORT_WAITING_ACK /*|| socket->waiting == TCP_PORT_WAITING_ACK_WAKING*/) { // This can be an ACK or really any other data packet, we just // need the ACK number if (ack_num == socket->seq_num) { // Update our status to show we're no longer waiting for an ACK socket->waiting = TCP_PORT_WAITING_NONE; // Wake up the thread waiting for the ACK semaphore_V(socket->wait_for_ack_sem); } else if (ack_num == socket->seq_num - 1) { // This follows the same logic from the comment block around // line 170. if (socket->timeout >= 6400) { socket->timeout /= 2; } } } // If it's an ACK, it requires no further processing if (header_reliable->message_type == MSG_ACK && data_len == 0) { free(arg); set_interrupt_level(prev_level); return; } // Check if it's a SYNACK we're waiting for if (socket->waiting == TCP_PORT_WAITING_SYNACK && header_reliable->message_type == MSG_SYNACK) { // We're now fully connected in our eyes, handshake complete socket->waiting = TCP_PORT_WAITING_NONE; semaphore_V(socket->wait_for_ack_sem); } // If we're here, the packet isnt an ACK or SYN, so we should ACK it // First check if we should increment our ack number if (seq_num == socket->ack_num + 1) { socket->ack_num++; } else { // It's a duplicate, don't add to queue duplicate = 1; } // Next, perform the ACK, don't incr the seq# transmit_packet(socket, socket->dst_addr, socket->dst_port, 0, MSG_ACK, 0, NULL, &error); /* Note: the code below handles FINs, and is also protected against * duplicate FINs inherently. The seq_num == ack_num check doesn't * guarantee it's not a duplicate; however, if we process one FIN, * then the socket's status is set to TCP_PORT_CLOSING. Therefore, * if we get another FIN, we can tell that the port is already closing * and we don't need to process it, which also ensures we don't * process duplicate FINs multiple times. We'll include the * duplicate == 0 check just for good measure, however. */ // We're required to close the conn 15s after we ACK a FIN, so do that here if (seq_num == socket->ack_num && header_reliable->message_type == MSG_FIN && socket->status != TCP_PORT_CLOSING && duplicate == 0) { socket->status = TCP_PORT_CLOSING; queue_append(sockets_to_delete, socket); semaphore_V(socket_needs_delete); } } else if (socket->status == TCP_PORT_LISTENING) { // Start a connection with the client if (header_reliable->message_type == MSG_SYN) { // Update socket's dst addr & dst port to the client's network_address_copy(src_addr, socket->dst_addr); socket->dst_port = src_port_number; // Set the status to connecting socket->status = TCP_PORT_CONNECTING; // Awake the create_server thread, it'll handle the rest semaphore_V(socket->wait_for_ack_sem); } } // Add the packet to the socket's packet queue if not duplicate & it's a data pkt if (duplicate == 0 && header_reliable->message_type == MSG_ACK && data_len != 0) { enqueued = 1; queue_append(socket->waiting_packets, arg); if (socket->data_len == 0) semaphore_V(socket->packet_ready); } // remember to free arg if we dont push this to the tcp recv queue break; } if (enqueued == 0) { free(arg); } // Restore the interrupt level set_interrupt_level(prev_level); return; }
/* * 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 minisocket; interrupt_level_t prev_level; int transmit_ret; int success = 0; if (error == NULL) { return NULL; } // Ensure the port is in the valid range if (port < TCP_MIN_SERVER || port > TCP_MAX_SERVER) { *error = SOCKET_INVALIDPARAMS; return NULL; } // We need to synchronize access to the server port range of the minisockets // pool, as we don't want another thread to try to create a socket on this port // concurrently semaphore_P(server_sem); // Ensure the port doesn't exist if (minisockets[port] != NULL) { *error = SOCKET_PORTINUSE; semaphore_V(server_sem); return NULL; } // Try to setup the socket struct minisocket = minisocket_setup_socket(port); if (minisocket == NULL) { *error = SOCKET_OUTOFMEMORY; semaphore_V(server_sem); return NULL; } // Set the port type minisocket->port_type = TCP_PORT_TYPE_SERVER; // Insert the minisocket into the array minisockets[port] = minisocket; // Ensure we don't have with the server semaphore, so other servers // can be made while we wait for a client semaphore_V(server_sem); // Continuously wait for a successful connection while (success == 0) { // Wait for a connection prev_level = set_interrupt_level(DISABLED); semaphore_P(minisocket->wait_for_ack_sem); set_interrupt_level(prev_level); // When we're awoken and have a client, set the status minisocket->status = TCP_PORT_CONNECTING; // And send the SYNACK sequence minisocket->ack_num++; // we're acknowledging the SYN, and also sending a packet that increments seq# transmit_ret = transmit_packet(minisocket, minisocket->dst_addr, minisocket->dst_port, 1, MSG_SYNACK, 0, NULL, error); if (transmit_ret == -1) { // This means we didn't get ACKs back, so this is a bad connection // Reset some port details and continue waiting minisocket->status = TCP_PORT_LISTENING; network_address_blankify(minisocket->dst_addr); minisocket->dst_port = 0; minisocket->ack_num--; minisocket->seq_num--; } else { // Established connection successfully minisocket->status = TCP_PORT_CONNECTED; success = 1; } } *error = SOCKET_NOERROR; return minisocket; }
/* * 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. */ int minisocket_send(minisocket_t socket, minimsg_t msg, int len, minisocket_error *error) { int portNumber; int sentLength; int sentData = 0; int maxDataSize; int check; //interrupt_level_t prev_level; if (error == NULL) return -1; if (socket == NULL || msg == NULL || len <= 0) { *error = SOCKET_INVALIDPARAMS; return -1; } portNumber = socket->port_number; if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_TO_CLOSE || minisockets[portNumber] == NULL) { *error = SOCKET_SENDERROR; return -1; } /*prev_level = set_interrupt_level(DISABLED); socket->num_waiting_on_mutex++; semaphore_P(socket->mutex); socket->num_waiting_on_mutex--; set_interrupt_level(prev_level); */ if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_TO_CLOSE || minisockets[portNumber] == NULL) { *error = SOCKET_SENDERROR; // semaphore_V(socket->mutex); return -1; } maxDataSize = MAX_NETWORK_PKT_SIZE - sizeof(struct mini_header_reliable); while (len > 0) { sentLength = (maxDataSize > len ? len : maxDataSize); check = transmit_packet(socket, socket->destination_addr, socket->destination_port, 1, MSG_ACK, sentLength, (msg+sentData), error); if (check == -1) { // semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return (sentData == 0 ? -1 : sentData); } len -= maxDataSize; sentData += sentLength; } // semaphore_V(socket->mutex); *error = SOCKET_NOERROR; return sentData; }
inline void receive_message( uint8_t uart, uint8_t* message, uint8_t message_size )//was inline { uint8_t command = message[VCP_COMMAND_FIELD]; uint8_t payload = message[VCP_PAYLOAD_FIELD]; switch(command) { case VCP_COMPONENT_ON: // Reset CDH IB Heartbeat timer if ( svit[payload].switch_num != SW_NULL ) { switch_on( svit[payload].switch_num ); svit[payload].switch_state = 1; } transmit_packet( uart, VCP_ACK, command ); break; case VCP_COMPONENT_OFF: if ( svit[payload].switch_num != SW_NULL ) { switch_off( svit[payload].switch_num ); svit[payload].switch_state = 0; } transmit_packet( uart, VCP_ACK, command ); break; case VCP_POWER_CYCLE: if ( svit[payload].switch_num != SW_NULL ) { switch_off( svit[payload].switch_num ); svit[payload].switch_state = 0; } _delay_us(1); if ( svit[payload].switch_num != SW_NULL ) { switch_on( svit[payload].switch_num ); svit[payload].switch_state = 1; } transmit_packet( uart, VCP_ACK, command ); break; case VCP_TORQ_CTRL: transmit_packet( uart, VCP_ACK, command ); break; case VCP_GET_TELEMETRY: transmit_packet( uart, VCP_POWER_TELEMETRY, 0); break; case VCP_FORCE_ON: if ( svit[payload].switch_num != SW_NULL ) { switch_on( svit[payload].switch_num ); svit[payload].switch_state = 1; svit[payload].force_on = 1; } transmit_packet( uart, VCP_ACK, command ); break; case VCP_CRIT_V_CHANGE: if ( svit[payload].switch_num != SW_NULL ) { svit[payload].V_upper_limit = message[VCP_PAYLOAD_FIELD + 1]; V_upper_val_change= message[VCP_PAYLOAD_FIELD + 1]; } //transmit_packet( uart, VCP_ACK, 0); break; case VCP_CRIT_I_CHANGE: if ( svit[payload].switch_num != SW_NULL ) { svit[payload].I_upper_limit = message[VCP_PAYLOAD_FIELD + 1]; I_upper_val_change= message[VCP_PAYLOAD_FIELD + 1]; } //transmit_packet( uart, VCP_ACK, 0); break; default: transmit_packet( uart, VCP_INVALID_COMMAND, 0 ); break; } }
void minisocket_destroy(minisocket_t minisocket, int send_FIN) { int i; int threads_waiting; int port_number; minisocket_error error; interrupt_level_t prev_level; if (minisocket == NULL) { return; } port_number = minisocket->port_number; semaphore_P(destroy_sem); // Basically, we need the socket's mutex to perform close, as we don't // want to close while someone is reading from the buffer or sending something. // However, the ms_receive() function acquires the mutex, and then may end // up waiting on socket->packet_ready. Therefore, we need to first signal // packet_ready before we can get the mutex in some cases. Only one thread // at a time is ever waiting on socket ready - we may end up letting two // pass it as we call it again in minisocket_destroy(), but we set the waiting // status to a value that the receive & send functions will detect and cause // their function calls to return with a failure. Note that we couldn't set // socket->status to TCP_PORT_CLOSING just yet, as that would cause the FIN // transmit to fail in our transmit_packet() function. Therefore, we just // created a new waiting status for it. minisocket->waiting = TCP_PORT_WAITING_CLOSE; // Check if this is already destroyed if (minisockets[port_number] == NULL) { return; } // Only one thread will ever be waiting on this (b/c it's in a mutex), so // just do it once to wake up any thread waiting on it in receive() semaphore_V(minisocket->packet_ready); // Wait for the socket mutex // This ensures that any currently executing send() finishes // This also ensures that anyone who is currently receiving from buffer finishes semaphore_P(minisocket->mutex); // Another thread already ran destroy, so we can just return // Not needed, but won't remove since we're running low on time and don't // want to break anything, just in case.... if (minisockets[port_number] == NULL) { return; } // Send a FIN if needed if (send_FIN == 1) { transmit_packet(minisocket, minisocket->dst_addr, minisocket->dst_port, 1, MSG_FIN, 0, NULL, &error); } // Eensure we have the status set correctly so the threads we wake know what // is happening minisocket->status = TCP_PORT_CLOSING; // Awake all threads waiting on mutex - they'll see we're closing and fail threads_waiting = minisocket->num_waiting_on_mutex; for (i = 0; i < threads_waiting; i++) { semaphore_V(minisocket->mutex); } // Remove the port from the minisockets pool minisockets[minisocket->port_number] = NULL; // NO NEED TO SYNCHRONIZE THIS STUFF WITH THE INTERRUPT HANDLER // This is because, if we get interrupted here, the above line will tell // the IH that this socket is closed, so it won't access any of the stuff // we're about to free // Free all the sems prev_level = set_interrupt_level(DISABLED); semaphore_destroy(minisocket->wait_for_ack_sem); semaphore_destroy(minisocket->mutex); semaphore_destroy(minisocket->packet_ready); // Free the data buffer if (minisocket->data_len != 0) free(minisocket->data_buffer); // Free the queue queue_free(minisocket->waiting_packets); set_interrupt_level(prev_level); // Free the socket itself free(minisocket); semaphore_V(destroy_sem); }
/* * 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. */ int minisocket_send(minisocket_t socket, minimsg_t msg, int len, minisocket_error *error) { int ret; int max_data_size; int data_sent = 0; int len_sent; int port_number; if (socket == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (msg == NULL) { *error = SOCKET_INVALIDPARAMS; return -1; } if (len <= 0) { *error = SOCKET_INVALIDPARAMS; return -1; } if (error == NULL) { return -1; } port_number = socket->port_number; // First, check if the socket is closing if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE || minisockets[port_number] == NULL) { *error = SOCKET_SENDERROR; return -1; } socket->num_waiting_on_mutex++; semaphore_P(socket->mutex); socket->num_waiting_on_mutex--; // Check again - maybe we were awoken because it's now closing if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE || minisockets[port_number] == NULL) { *error = SOCKET_SENDERROR; semaphore_V(socket->mutex); return -1; } // Determine the max data size max_data_size = MAX_NETWORK_PKT_SIZE - sizeof(struct mini_header_reliable); if (len > max_data_size) { // Partition the large message into smaller packets while (len > 0) { // See how much we can fit into the next packet len_sent = (max_data_size > len ? len : max_data_size); // Send the packet ret = transmit_packet(socket, socket->dst_addr, socket->dst_port, 1, MSG_ACK, len_sent, msg+data_sent, error); // Check for errors if (ret == -1) { // If we can't reach the host, close the connection semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return (data_sent == 0 ? -1 : data_sent); } // Update the amount of data left to send len -= max_data_size; // can also be - len_sent, doesn't matter // Update the number of bytes we've sent - also used w/ msg as a ptr data_sent += len_sent; } } else { // This can be transmitted as a single packet ret = transmit_packet(socket, socket->dst_addr, socket->dst_port, 1, MSG_ACK, len, msg, error); if (ret == -1) { // If we can't reach the host, close the connection semaphore_V(socket->mutex); minisocket_destroy(socket, 0); return -1; } data_sent = len; } semaphore_V(socket->mutex); *error = SOCKET_NOERROR; return data_sent; }
/* * 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) { int port_number; int num_client_ports = TCP_MAX_CLIENT - TCP_MIN_CLIENT + 1; int adjusted_port; int found_port = 0; int i; int transmit_ret; minisocket_t minisocket; if (error == NULL) { return NULL; } // Synchornize access to the client port range of the minisocket pool semaphore_P(client_sem); // Find a free client port to use if (client_wrapped == 0) { // If we haven't wrapped around yet, just pick the next port port_number = current_client_port++; // Check if we need to wrap around if (current_client_port > TCP_MAX_CLIENT) { client_wrapped = 1; current_client_port = TCP_MIN_CLIENT; } } else { for (i = 0; i < num_client_ports; i++) { // Sorry this goes over 80 characters, but it's easier to see on one line adjusted_port = (((current_client_port - TCP_MIN_CLIENT)+i) % num_client_ports) + TCP_MIN_CLIENT; if (minisockets[adjusted_port] == NULL) { current_client_port = adjusted_port; found_port = 1; break; } } // If we coulnd't find a port, return NULL if (found_port == 0) { *error = SOCKET_NOMOREPORTS; semaphore_V(client_sem); return NULL; } // Set the port number port_number = current_client_port++; } // Create the socket minisocket = minisocket_setup_socket(port_number); if (minisocket == NULL) { *error = SOCKET_OUTOFMEMORY; semaphore_V(client_sem); return NULL; } // Set the port type and client variables minisocket->port_type = TCP_PORT_TYPE_CLIENT; network_address_copy(addr, minisocket->dst_addr); minisocket->dst_port = port; // Set the port in the array minisockets[port_number] = minisocket; // Prepare to send a SYN and connect minisocket->status = TCP_PORT_CONNECTING; // Release the sem as soon as possible semaphore_V(client_sem); // Send the SYN - incr the seq # too, and do retransmission sequence pending SYNACK transmit_ret = transmit_packet(minisocket, addr, port, 1, MSG_SYN, 0, NULL, error); // Check if we got a FIN - which means the server is busy with another client if (minisocket->status == TCP_PORT_COULDNT_CONNECT) { // The packet process thread will make the status this value if we were // connecting and got a FIN minisockets[port] = NULL; free(minisocket); *error = SOCKET_BUSY; return NULL; } // Check if the server didn't respond if (transmit_ret == -1) { // The error code is set by transmit_packet(), so just clean up & return minisockets[port] = NULL; free(minisocket); return NULL; } minisocket->status = TCP_PORT_CONNECTED; *error = SOCKET_NOERROR; return minisocket; }
/* * 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 newMinisocket; int ack_check; int connected = 1; network_interrupt_arg_t *arg; mini_header_reliable_t header; if (error == NULL) return NULL; if (port < TCP_MINIMUM_SERVER || port > TCP_MAXIMUM_SERVER) { *error = SOCKET_INVALIDPARAMS; return NULL; } semaphore_P(server_mutex); //Checks if port already exists if (minisockets[port] != NULL) { *error = SOCKET_PORTINUSE; semaphore_V(server_mutex); return NULL; } newMinisocket = minisocket_create_socket(port); if (newMinisocket == NULL) { *error = SOCKET_OUTOFMEMORY; semaphore_V(server_mutex); return NULL; } newMinisocket->port_type = TCP_PORT_TYPE_SERVER; minisockets[port] = newMinisocket; semaphore_V(server_mutex); while (connected == 1) { semaphore_P(newMinisocket->packet_ready); queue_dequeue(newMinisocket->waiting_packets, (void **) &arg); header = (mini_header_reliable_t) arg->buffer; if (header->message_type != MSG_SYN) continue; newMinisocket->status = TCP_PORT_CONNECTING; unpack_address(header->source_address, newMinisocket->destination_addr); newMinisocket->destination_port = unpack_unsigned_short(header->source_port); ack_check = transmit_packet(newMinisocket, newMinisocket->destination_addr, newMinisocket->destination_port, 1, MSG_SYNACK, 0, NULL, error); if (ack_check == -1) { newMinisocket->status = TCP_PORT_LISTENING; network_address_blankify(newMinisocket->destination_addr); newMinisocket->destination_port = 0; } else { newMinisocket->status = TCP_PORT_CONNECTED; connected = 0; } } *error = SOCKET_NOERROR; return newMinisocket; }
/* * 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 newMinisocket; int totalClientPorts = TCP_MAXIMUM_CLIENT - TCP_MINIMUM_CLIENT + 1; int convertedPortNumber = (currentClientPort % totalClientPorts) + TCP_MINIMUM_CLIENT; int i = 1; int transmitCheck; if (error == NULL) return NULL; semaphore_P(client_mutex); while (i < totalClientPorts && (minisockets[convertedPortNumber] != NULL)) { convertedPortNumber = ((currentClientPort + i) % totalClientPorts) + TCP_MINIMUM_CLIENT; i++; } currentClientPort += (i-1); if (minisockets[convertedPortNumber] != NULL) { *error = SOCKET_NOMOREPORTS; semaphore_V(client_mutex); return NULL; } newMinisocket = minisocket_create_socket(convertedPortNumber); if (newMinisocket == NULL) { *error = SOCKET_OUTOFMEMORY; semaphore_V(client_mutex); return NULL; } newMinisocket->port_type = TCP_PORT_TYPE_CLIENT; network_address_copy(addr, newMinisocket->destination_addr); newMinisocket->destination_port = convertedPortNumber; minisockets[convertedPortNumber] = newMinisocket; newMinisocket->status = TCP_PORT_CONNECTING; semaphore_V(client_mutex); transmitCheck = transmit_packet(newMinisocket, addr, port, 1, MSG_SYN, 0, NULL, error); if (transmitCheck == -1) { //*error set by transmit_packet() minisockets[convertedPortNumber] = NULL; free(newMinisocket); return NULL; } newMinisocket->ack_number++; //newMinisocket->waiting = TCP_PORT_WAITING_SYNACK; //semaphore_P(newMinisocket->wait_for_ack_semaphore); transmitCheck = transmit_packet(newMinisocket, addr, port, 1, MSG_ACK, 0, NULL, error); if (transmitCheck == -1) { minisockets[convertedPortNumber] = NULL; free(newMinisocket); return NULL; } newMinisocket->status = TCP_PORT_CONNECTED; *error = SOCKET_NOERROR; return newMinisocket; }
/* * Process an interrupt. * Return nonzero when there was some activity. */ static unsigned handle_interrupt(eth_t *u) { unsigned active = 0; ARM_NVIC_ICPR(0) = 1 << ETH_IRQ; u->intr_flags = ARM_ETH->IFR; if (u->intr_flags & ARM_ETH_XF_UNDF) { ARM_ETH->R_CFG &= ~ARM_ETH_EN; ARM_ETH->X_CFG &= ~ARM_ETH_EN; ARM_ETH->G_CFG_HI |= ARM_ETH_XRST; ARM_ETH->X_TAIL = ARM_ETH->X_HEAD; ARM_ETH->G_CFG_HI &= ~ARM_ETH_XRST; ARM_ETH->X_CFG |= ARM_ETH_EN; ARM_ETH->R_CFG |= ARM_ETH_EN; } /* Обработка приёма пакета */ //if (ARM_ETH->X_TAIL != ARM_ETH->X_HEAD) { // /* Если есть данные, прочитать пакет */ // receive_packet(u); // active++; //} if (ARM_ETH->R_TAIL != ARM_ETH->R_HEAD) { /* Если есть данные, прочитать пакет */ while (ARM_ETH->STAT & 0xE0) { receive_packet(u); ARM_ETH->IFR |= ARM_ETH_RF_OK; } active++; } u->intr_flags = ARM_ETH->IFR; if (u->intr_flags & ARM_ETH_MISSED_F) { ARM_ETH->IFR |= ARM_ETH_MISSED_F; //ARM_ETH->R_HEAD = ARM_ETH->R_TAIL; //if (ARM_ETH->STAT & 0xE0){ // ARM_ETH->STAT -= 0x20; // ++u->in_missed_and_rx; //} //if(ARM_ETH->STAT & ~0xE0) // ARM_ETH->R_HEAD = ARM_ETH->R_TAIL; //chip_read_rxfifo(u, ARM_ETH_PKT_LENGTH(ARM_ETH_RX_FIFO)); ++u->netif.in_discards; ++u->in_missed; //ARM_NVIC_ICPR(0) = 1 << ETH_IRQ; //return 0; } /* успешная отправка */ if (u->intr_flags & ARM_ETH_XF_OK) { ARM_ETH->IFR |= ARM_ETH_XF_OK; /* debug_printf("Transmot ok. ARM_ETH->IFR = <0x%04x>\n", u->intr_flags); */ //#ifdef ETH_FIFO // unsigned tmp = ARM_ETH_TX_FIFO; //#endif /* Извлекаем следующий пакет из очереди. */ buf_t *p = buf_queue_get(&u->outq); if (p) { /* Передаём следующий пакет. */ transmit_packet(u, p); netif_free_buf (&u->netif, p); } active++; } if (u->intr_flags & ARM_ETH_XF_ERR) { ARM_ETH->IFR |= ARM_ETH_XF_ERR; ++u->netif.out_errors; //#ifdef ETH_FIFO // unsigned tmp = ARM_ETH_TX_FIFO; //#endif /* debug_printf("Error Tx on transmiteARM_ETH->IFR = <0x%04x>\n", u->intr_flags); */ } /* Подсчитываем коллизии. */ if (u->intr_flags & ARM_ETH_LC) { ARM_ETH->IFR |= ARM_ETH_LC; ++u->netif.out_collisions; /* debug_printf("Error collisions\n"); */ } //ARM_NVIC_ICPR(0) = 1 << ETH_IRQ; return active; }
static int sys_trans_pkt(void *va, size_t len) { user_mem_assert(curenv, va, len, PTE_P | PTE_U); return transmit_packet(va, len); }
void network_handler(network_interrupt_arg_t* arg) { // Used to extract the network address out of the interrupt argument //network_address_t addr; // The source port number - this is really the remote unbound port number int src_port_number; // The port number the packet was sent to int dst_port_number; // Used to store the header data for UDP/minimsgs mini_header_t header; // This is used to extract the sender's address from the header network_address_t src_addr; // This is used to extract the destination (our) address from the header network_address_t dst_addr; // Disable interrupts... interrupt_level_t prev_level = set_interrupt_level(DISABLED); // This is used to check our own address for sanity checks network_address_t my_addr; // This will store the total packet size int packet_size; // This will store the size of the data in the packet int data_len; // This will store the header of a TCP packet mini_header_reliable_t header_reliable; // This will store the ACK number of a TCP packet int ack_num; // This will store the Sequence number of a TCP packet int seq_num; // This will store the socket minisocket_t socket; // This will store the TCP error minisocket_error error; // This will be used to indicate whether the TCP packet is a duplicate or not int duplicate = 0; // Check the protocol switch (arg->buffer[0]) { case (char) PROTOCOL_MINIDATAGRAM: // Extract data from the network interrupt arg //network_address_copy(arg->addr, addr); // Get the header struct, unpack the parameters header = (mini_header_t) &arg->buffer; dst_port_number = (int) unpack_unsigned_short(header->destination_port); // Ensure the port number is valid if (dst_port_number < MIN_UNBOUND || dst_port_number > MAX_UNBOUND) { free(arg); set_interrupt_level(prev_level); return; } // Then ensure the miniport exists if (miniports[dst_port_number] == NULL) { free(arg); set_interrupt_level(prev_level); return; } // Add the arg to the queue queue_append(miniports[dst_port_number]->port_data.unbound.data_queue, arg); // Wake up thread that's waiting to receive - sem_V() semaphore_V(miniports[dst_port_number]->port_data.unbound.data_ready); break; case PROTOCOL_MINISTREAM: // Get the total size of the packet and data length packet_size = arg->size; data_len = packet_size - sizeof(struct mini_header_reliable); // Get the header and extract information header_reliable = (mini_header_reliable_t) arg->buffer; src_port_number = (int) unpack_unsigned_short(header_reliable->source_port); dst_port_number = (int) unpack_unsigned_short(header_reliable->destination_port); unpack_address(header_reliable->source_address, src_addr); unpack_address(header_reliable->destination_address, dst_addr); seq_num = (int) unpack_unsigned_int(header_reliable->seq_number); ack_num = (int) unpack_unsigned_int(header_reliable->ack_number); // Don't respond if socket doesn't exist - will trigger SOCKET_NOSERVER for client if (minisockets[dst_port_number] == NULL) { set_interrupt_level(prev_level); return; } // Get the socket in question socket = minisockets[dst_port_number]; // Check if packet was meant for us - ignore if not network_get_my_address(my_addr); if (network_compare_network_addresses(my_addr, dst_addr) == 0) { set_interrupt_level(prev_level); return; } if (socket->status == TCP_PORT_CLOSING || socket->waiting == TCP_PORT_WAITING_CLOSE) { set_interrupt_level(prev_level); return; } // Packet handling for established connections if (socket->status != TCP_PORT_LISTENING) { // Ensure source address is correct - ignore if not if (network_compare_network_addresses(src_addr, socket->dst_addr) == 0 || src_port_number != socket->dst_port) { if (header_reliable->message_type == MSG_SYN) { // A client is trying to connect to an already-connected port // Send them a FIN, which will result in their client // returning a SOCKET_BUSY error transmit_packet(socket, src_addr, src_port_number, 0, MSG_FIN, 0, NULL, &error); } set_interrupt_level(prev_level); return; } /* We got a duplicate SYN and need to ensure that the host gets * another SYNACK. The thread that sent the SYNACK will currently * be retransmitting the SYNACKs, as it didn't get an ACK. If it * was done with the retransmission sequence, the socket would be * closed, and this would therefore not have gotten this far. * HOWEVER: * If the retransmission sequence already sent its LAST * retransmission and was waiting on it, then this duplicate SYN * will NOT get a SYNACK, as the retransmission is just waiting * out the last alarm before it ends. Therefore, if this is the case, * reset the timeout to the last timeout value so it sends just * one more SYNACK. * * Professor Sirer mentioned that this isn't even necessary, * but this does seem to make the code follow the specs more. */ if (header_reliable->message_type == MSG_SYN) { // If the timeout is at 6400, then it's the last transmission // but it hasn't been doubled yet. If it's 12800, it's just // been doubled. In each case we want to keep it at 6400 // at the end of the while loop, so we set the timeout to half // it's current value. if (socket->timeout >= 6400) { socket->timeout /= 2; } set_interrupt_level(prev_level); return; } // If we were trying to connect and got a FIN, that means the socket's busy if (socket->status == TCP_PORT_CONNECTING && header_reliable->message_type == MSG_FIN) { // Not connected, got a FIN - that means we couldnt start connection b/c it was busy // This will let the client function infer that socket->status = TCP_PORT_COULDNT_CONNECT; // Wake it up so it can end the retransmission sequence semaphore_V(socket->wait_for_ack_sem); set_interrupt_level(prev_level); return; } /* Note: the code below handles ACKs. It also inherently deals with * duplicate ACKs. We have a status code that indicates whether the socket * is waiting for an ACK or not. If it's set and we get an ACK (or any * packet) with the right ACK number, we can process it. However, if our * status indicates we're NOT waiting for an ACK, we can infer from the * fact that window_size = 1 that we already got the only ACK we could've * been expecting, and this new one is therefore a duplicate. */ // If we're waiting on an ACK if (socket->waiting == TCP_PORT_WAITING_ACK /*|| socket->waiting == TCP_PORT_WAITING_ACK_WAKING*/) { // This can be an ACK or really any other data packet, we just // need the ACK number if (ack_num == socket->seq_num) { // Update our status to show we're no longer waiting for an ACK socket->waiting = TCP_PORT_WAITING_NONE; // Wake up the thread waiting for the ACK semaphore_V(socket->wait_for_ack_sem); } else if (ack_num == socket->seq_num - 1) { // This follows the same logic from the comment block around // line 170. if (socket->timeout >= 6400) { socket->timeout /= 2; } } } // If it's an ACK, it requires no further processing if (header_reliable->message_type == MSG_ACK && data_len == 0) { set_interrupt_level(prev_level); return; } // Check if it's a SYNACK we're waiting for if (socket->waiting == TCP_PORT_WAITING_SYNACK && header_reliable->message_type == MSG_SYNACK) { // We're now fully connected in our eyes, handshake complete socket->waiting = TCP_PORT_WAITING_NONE; semaphore_V(socket->wait_for_ack_sem); } // If we're here, the packet isnt an ACK or SYN, so we should ACK it // First check if we should increment our ack number if (seq_num == socket->ack_num + 1) { socket->ack_num++; } else { // It's a duplicate, don't add to queue duplicate = 1; } // Next, perform the ACK, don't incr the seq# transmit_packet(socket, socket->dst_addr, socket->dst_port, 0, MSG_ACK, 0, NULL, &error); /* Note: the code below handles FINs, and is also protected against * duplicate FINs inherently. The seq_num == ack_num check doesn't * guarantee it's not a duplicate; however, if we process one FIN, * then the socket's status is set to TCP_PORT_CLOSING. Therefore, * if we get another FIN, we can tell that the port is already closing * and we don't need to process it, which also ensures we don't * process duplicate FINs multiple times. We'll include the * duplicate == 0 check just for good measure, however. */ // We're required to close the conn 15s after we ACK a FIN, so do that here if (seq_num == socket->ack_num && header_reliable->message_type == MSG_FIN && socket->status != TCP_PORT_CLOSING && duplicate == 0) { socket->status = TCP_PORT_CLOSING; queue_append(sockets_to_delete, socket); semaphore_V(socket_needs_delete); } } else if (socket->status == TCP_PORT_LISTENING) { // Start a connection with the client if (header_reliable->message_type == MSG_SYN) { // Update socket's dst addr & dst port to the client's network_address_copy(src_addr, socket->dst_addr); socket->dst_port = src_port_number; // Set the status to connecting socket->status = TCP_PORT_CONNECTING; // Awake the create_server thread, it'll handle the rest semaphore_V(socket->wait_for_ack_sem); } } // Add the packet to the socket's packet queue if not duplicate & it's a data pkt if (duplicate == 0 && header_reliable->message_type == MSG_ACK && data_len != 0) { queue_append(socket->waiting_packets, arg); if (socket->data_len == 0) semaphore_V(socket->packet_ready); } // remember to free arg if we dont push this to the tcp recv queue break; } // Restore the interrupt level set_interrupt_level(prev_level); return; }