void socket_free(gpointer data) { Socket* socket = data; MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); if(socket->peerString) { g_free(socket->peerString); } if(socket->boundString) { g_free(socket->boundString); } if(socket->unixPath) { g_free(socket->unixPath); } while(g_queue_get_length(socket->inputBuffer) > 0) { packet_unref(g_queue_pop_head(socket->inputBuffer)); } g_queue_free(socket->inputBuffer); while(g_queue_get_length(socket->outputBuffer) > 0) { packet_unref(g_queue_pop_head(socket->outputBuffer)); } g_queue_free(socket->outputBuffer); MAGIC_CLEAR(socket); socket->vtable->free((Descriptor*)socket); }
void tcp_free(TCP* tcp) { MAGIC_ASSERT(tcp); while(g_queue_get_length(tcp->throttledOutput) > 0) { packet_unref(g_queue_pop_head(tcp->throttledOutput)); } g_queue_free(tcp->throttledOutput); while(g_queue_get_length(tcp->unorderedInput) > 0) { packet_unref(g_queue_pop_head(tcp->unorderedInput)); } g_queue_free(tcp->unorderedInput); g_hash_table_destroy(tcp->retransmission); if(tcp->child) { MAGIC_ASSERT(tcp->child); MAGIC_ASSERT(tcp->child->parent); MAGIC_ASSERT(tcp->child->parent->server); /* remove parents reference to child, if it exists */ g_hash_table_remove(tcp->child->parent->server->children, &(tcp->child->key)); _tcpchild_free(tcp->child); } if(tcp->server) { _tcpserver_free(tcp->server); } MAGIC_CLEAR(tcp); g_free(tcp); }
void networkinterface_free(NetworkInterface* interface) { MAGIC_ASSERT(interface); /* unref all packets sitting in our input buffer */ while(interface->inBuffer && !g_queue_is_empty(interface->inBuffer)) { Packet* packet = g_queue_pop_head(interface->inBuffer); packet_unref(packet); } g_queue_free(interface->inBuffer); /* unref all sockets wanting to send */ while(interface->rrQueue && !g_queue_is_empty(interface->rrQueue)) { Socket* socket = g_queue_pop_head(interface->rrQueue); descriptor_unref(socket); } g_queue_free(interface->rrQueue); priorityqueue_free(interface->fifoQueue); g_hash_table_destroy(interface->boundSockets); dns_deregister(worker_getDNS(), interface->address); address_unref(interface->address); if(interface->pcapFile) { fclose(interface->pcapFile); } MAGIC_CLEAR(interface); g_free(interface); }
static void _networkinterface_scheduleNextReceive(NetworkInterface* interface) { /* the next packets need to be received and processed */ SimulationTime batchTime = worker_getConfig()->interfaceBatchTime; /* receive packets in batches */ while(!g_queue_is_empty(interface->inBuffer) && interface->receiveNanosecondsConsumed <= batchTime) { /* get the next packet */ Packet* packet = g_queue_pop_head(interface->inBuffer); utility_assert(packet); /* successfully received */ packet_addDeliveryStatus(packet, PDS_RCV_INTERFACE_RECEIVED); _networkinterface_pcapWritePacket(interface, packet); /* free up buffer space */ guint length = packet_getPayloadLength(packet) + packet_getHeaderSize(packet); interface->inBufferLength -= length; /* calculate how long it took to 'receive' this packet */ interface->receiveNanosecondsConsumed += (length * interface->timePerByteDown); /* hand it off to the correct socket layer */ gint key = packet_getDestinationAssociationKey(packet); Socket* socket = g_hash_table_lookup(interface->boundSockets, GINT_TO_POINTER(key)); /* if the socket closed, just drop the packet */ gint socketHandle = -1; if(socket) { socketHandle = *descriptor_getHandleReference((Descriptor*)socket); socket_pushInPacket(socket, packet); } else { packet_addDeliveryStatus(packet, PDS_RCV_INTERFACE_DROPPED); } packet_unref(packet); /* count our bandwidth usage by interface, and by socket handle if possible */ tracker_addInputBytes(host_getTracker(worker_getCurrentHost()),(guint64)length, socketHandle); } /* * we need to call back and try to receive more, even if we didnt consume all * of our batch time, because we might have more packets to receive then. */ SimulationTime receiveTime = (SimulationTime) floor(interface->receiveNanosecondsConsumed); if(receiveTime >= SIMTIME_ONE_NANOSECOND) { /* we are 'receiving' the packets */ interface->flags |= NIF_RECEIVING; /* call back when the packets are 'received' */ InterfaceReceivedEvent* event = interfacereceived_new(interface); /* event destination is our node */ worker_scheduleEvent((Event*)event, receiveTime, 0); } }
gssize udp_receiveUserData(UDP* udp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(udp); Packet* packet = socket_removeFromInputBuffer((Socket*)udp); if(!packet) { return -1; } /* copy lesser of requested and available amount to application buffer */ guint packetLength = packet_getPayloadLength(packet); gsize copyLength = MIN(nBytes, packetLength); guint bytesCopied = packet_copyPayload(packet, 0, buffer, copyLength); utility_assert(bytesCopied == copyLength); packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_DELIVERED); /* fill in address info */ if(ip) { *ip = packet_getSourceIP(packet); } if(port) { *port = packet_getSourcePort(packet); } /* destroy packet, throwing away any bytes not claimed by the app */ packet_unref(packet); /* update the tracker output buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Socket* socket = (Socket* )udp; Descriptor* descriptor = (Descriptor *)socket; gsize outLength = socket_getOutputBufferLength(socket); gsize outSize = socket_getOutputBufferSize(socket); tracker_updateSocketOutputBuffer(tracker, descriptor->handle, outLength, outSize); debug("user read %u inbound UDP bytes", bytesCopied); return (gssize)bytesCopied; }
/* return TRUE if the packet should be retransmitted */ gboolean tcp_processPacket(TCP* tcp, Packet* packet) { MAGIC_ASSERT(tcp); /* fetch the TCP info from the packet */ PacketTCPHeader header; packet_getTCPHeader(packet, &header); guint packetLength = packet_getPayloadLength(packet); /* if we run a server, the packet could be for an existing child */ tcp = _tcp_getSourceTCP(tcp, header.sourceIP, header.sourcePort); /* now we have the true TCP for the packet */ MAGIC_ASSERT(tcp); /* print packet info for debugging */ debug("%s <-> %s: processing packet# %u length %u", tcp->super.boundString, tcp->super.peerString, header.sequence, packetLength); /* if packet is reset, don't process */ if(header.flags & PTCP_RST) { /* @todo: not sure if this is handled correctly */ debug("received RESET packet"); if(!(tcp->state & TCPS_LISTEN) && !(tcp->error & TCPE_CONNECTION_RESET)) { tcp->error |= TCPE_CONNECTION_RESET; tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_TIMEWAIT); /* it will send no more user data after what we have now */ tcp->receive.end = tcp->receive.next; } packet_unref(packet); return FALSE; } /* if we are a server, we have to remember who we got this from so we can * respond back to them. this is because we could be bound to several * interfaces and otherwise cant decide which to send on. */ if(tcp->server) { tcp->server->lastPeerIP = header.sourceIP; tcp->server->lastPeerPort = header.sourcePort; tcp->server->lastIP = header.destinationIP; } /* go through the state machine, tracking processing and response */ gboolean wasProcessed = FALSE; enum ProtocolTCPFlags responseFlags = PTCP_NONE; switch(tcp->state) { case TCPS_LISTEN: { /* receive SYN, send SYNACK, move to SYNRECEIVED */ if(header.flags & PTCP_SYN) { MAGIC_ASSERT(tcp->server); wasProcessed = TRUE; /* we need to multiplex a new child */ Node* node = worker_getPrivate()->cached_node; gint multiplexedHandle = node_createDescriptor(node, DT_TCPSOCKET); TCP* multiplexed = (TCP*) node_lookupDescriptor(node, multiplexedHandle); multiplexed->child = _tcpchild_new(multiplexed, tcp, header.sourceIP, header.sourcePort); g_assert(g_hash_table_lookup(tcp->server->children, &(multiplexed->child->key)) == NULL); g_hash_table_replace(tcp->server->children, &(multiplexed->child->key), multiplexed->child); multiplexed->receive.start = header.sequence; multiplexed->receive.next = multiplexed->receive.start + 1; debug("%s <-> %s: server multiplexed child socket %s <-> %s", tcp->super.boundString, tcp->super.peerString, multiplexed->super.boundString, multiplexed->super.peerString); _tcp_setState(multiplexed, TCPS_SYNRECEIVED); /* parent will send response */ responseFlags = PTCP_SYN|PTCP_ACK; } break; } case TCPS_SYNSENT: { /* receive SYNACK, send ACK, move to ESTABLISHED */ if((header.flags & PTCP_SYN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; tcp->receive.start = header.sequence; tcp->receive.next = tcp->receive.start + 1; responseFlags |= PTCP_ACK; _tcp_setState(tcp, TCPS_ESTABLISHED); } /* receive SYN, send ACK, move to SYNRECEIVED (simultaneous open) */ else if(header.flags & PTCP_SYN) { wasProcessed = TRUE; tcp->receive.start = header.sequence; tcp->receive.next = tcp->receive.start + 1; responseFlags |= PTCP_ACK; _tcp_setState(tcp, TCPS_SYNRECEIVED); } break; } case TCPS_SYNRECEIVED: { /* receive ACK, move to ESTABLISHED */ if(header.flags & PTCP_ACK) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_ESTABLISHED); /* if this is a child, mark it accordingly */ if(tcp->child) { tcp->child->state = TCPCS_PENDING; g_queue_push_tail(tcp->child->parent->server->pending, tcp->child); /* user should accept new child from parent */ descriptor_adjustStatus(&(tcp->child->parent->super.super.super), DS_READABLE, TRUE); } } break; } case TCPS_ESTABLISHED: { /* receive FIN, send FINACK, move to CLOSEWAIT */ if(header.flags & PTCP_FIN) { wasProcessed = TRUE; /* other side of connections closed */ tcp->flags |= TCPF_REMOTE_CLOSED; responseFlags |= (PTCP_FIN|PTCP_ACK); _tcp_setState(tcp, TCPS_CLOSEWAIT); /* remote will send us no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_FINWAIT1: { /* receive FINACK, move to FINWAIT2 */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_FINWAIT2); } /* receive FIN, send FINACK, move to CLOSING (simultaneous close) */ else if(header.flags & PTCP_FIN) { wasProcessed = TRUE; responseFlags |= (PTCP_FIN|PTCP_ACK); tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_CLOSING); /* it will send no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_FINWAIT2: { /* receive FIN, send FINACK, move to TIMEWAIT */ if(header.flags & PTCP_FIN) { wasProcessed = TRUE; responseFlags |= (PTCP_FIN|PTCP_ACK); tcp->flags |= TCPF_REMOTE_CLOSED; _tcp_setState(tcp, TCPS_TIMEWAIT); /* it will send no more user data after this sequence */ tcp->receive.end = header.sequence; } break; } case TCPS_CLOSING: { /* receive FINACK, move to TIMEWAIT */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_TIMEWAIT); } break; } case TCPS_TIMEWAIT: { break; } case TCPS_CLOSEWAIT: { break; } case TCPS_LASTACK: { /* receive FINACK, move to CLOSED */ if((header.flags & PTCP_FIN) && (header.flags & PTCP_ACK)) { wasProcessed = TRUE; _tcp_setState(tcp, TCPS_CLOSED); /* we closed, cant use tcp anymore, no retransmit */ packet_unref(packet); return FALSE; } break; } case TCPS_CLOSED: { /* stray packet, drop without retransmit */ packet_unref(packet); return FALSE; break; } default: { break; } } gint nPacketsAcked = 0; /* check if we can update some TCP control info */ if(header.flags & PTCP_ACK) { wasProcessed = TRUE; if((header.acknowledgement > tcp->send.unacked) && (header.acknowledgement <= tcp->send.next)) { /* some data we sent got acknowledged */ nPacketsAcked = header.acknowledgement - tcp->send.unacked; /* the packets just acked are 'released' from retransmit queue */ for(guint i = tcp->send.unacked; i < header.acknowledgement; i++) { _tcp_removeRetransmit(tcp, i); } tcp->send.unacked = header.acknowledgement; /* update congestion window and keep track of when it was updated */ tcp->congestion.lastWindow = header.window; tcp->congestion.lastSequence = header.sequence; tcp->congestion.lastAcknowledgement = header.acknowledgement; } } gboolean doRetransmitData = FALSE; /* check if the packet carries user data for us */ if(packetLength > 0) { /* it has data, check if its in the correct range */ if(header.sequence >= (tcp->receive.next + tcp->receive.window)) { /* its too far ahead to accept now, but they should re-send it */ wasProcessed = TRUE; doRetransmitData = TRUE; } else if(header.sequence >= tcp->receive.next) { /* its in our window, so we can accept the data */ wasProcessed = TRUE; /* * if this is THE next packet, we MUST accept it to avoid * deadlocks (unless we are blocked b/c user should read) */ gboolean isNextPacket = (header.sequence == tcp->receive.next) ? TRUE : FALSE; gboolean waitingUserRead = (socket_getInputBufferSpace(&(tcp->super)) > 0) ? TRUE : FALSE; gboolean packetFits = (packetLength <= _tcp_getBufferSpaceIn(tcp)) ? TRUE : FALSE; if((isNextPacket && !waitingUserRead) || (packetFits)) { /* make sure its in order */ _tcp_bufferPacketIn(tcp, packet); } else { debug("no space for packet even though its in our window"); doRetransmitData = TRUE; } } } /* if it is a spurious packet, send a reset */ if(!wasProcessed) { g_assert(responseFlags == PTCP_NONE); responseFlags = PTCP_RST; } /* try to update congestion window based on potentially new info */ _tcp_updateCongestionWindow(tcp, nPacketsAcked); /* now flush as many packets as we can to socket */ _tcp_flush(tcp); /* send ack if they need updates but we didn't send any yet (selective acks) */ if((tcp->receive.next > tcp->send.lastAcknowledgement) || (tcp->receive.window != tcp->send.lastWindow)) { responseFlags |= PTCP_ACK; } /* send control packet if we have one */ if(responseFlags != PTCP_NONE) { debug("%s <-> %s: sending response control packet", tcp->super.boundString, tcp->super.peerString); Packet* response = _tcp_createPacket(tcp, responseFlags, NULL, 0); _tcp_bufferPacketOut(tcp, response); _tcp_flush(tcp); } /* we should free packets that are done but were not buffered */ if(!doRetransmitData && packetLength <= 0) { packet_unref(packet); } return doRetransmitData; }
gssize tcp_receiveUserData(TCP* tcp, gpointer buffer, gsize nBytes, in_addr_t* ip, in_port_t* port) { MAGIC_ASSERT(tcp); /* * TODO * We call descriptor_adjustStatus too many times here, to handle the readable * state of the socket at times when we have a partially read packet. Consider * adding a required hook for socket subclasses so the socket layer can * query TCP for readability status. */ /* make sure we pull in all readable user data */ _tcp_flush(tcp); gsize remaining = nBytes; gsize bytesCopied = 0; gsize totalCopied = 0; gsize offset = 0; gsize copyLength = 0; /* check if we have a partial packet waiting to get finished */ if(remaining > 0 && tcp->partialUserDataPacket) { guint partialLength = packet_getPayloadLength(tcp->partialUserDataPacket); guint partialBytes = partialLength - tcp->partialOffset; g_assert(partialBytes > 0); copyLength = MIN(partialBytes, remaining); bytesCopied = packet_copyPayload(tcp->partialUserDataPacket, tcp->partialOffset, buffer, copyLength); totalCopied += bytesCopied; remaining -= bytesCopied; offset += bytesCopied; if(bytesCopied >= partialBytes) { /* we finished off the partial packet */ packet_unref(tcp->partialUserDataPacket); tcp->partialUserDataPacket = NULL; tcp->partialOffset = 0; } else { /* still more partial bytes left */ tcp->partialOffset += bytesCopied; g_assert(remaining == 0); } } while(remaining > 0) { /* if we get here, we should have read the partial packet above, or * broken out below */ g_assert(tcp->partialUserDataPacket == NULL); g_assert(tcp->partialOffset == 0); /* get the next buffered packet - we'll always need it. * this could mark the socket as unreadable if this is its last packet.*/ Packet* packet = socket_removeFromInputBuffer((Socket*)tcp); if(!packet) { /* no more packets or partial packets */ break; } guint packetLength = packet_getPayloadLength(packet); copyLength = MIN(packetLength, remaining); bytesCopied = packet_copyPayload(packet, 0, buffer + offset, copyLength); totalCopied += bytesCopied; remaining -= bytesCopied; offset += bytesCopied; if(bytesCopied < packetLength) { /* we were only able to read part of this packet */ tcp->partialUserDataPacket = packet; tcp->partialOffset = bytesCopied; break; } /* we read the entire packet, and are now finished with it */ packet_unref(packet); } /* now we update readability of the socket */ if((tcp->super.inputBufferLength > 0) || (tcp->partialUserDataPacket != NULL)) { /* we still have readable data */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE); } else { /* all of our ordered user data has been read */ if((tcp->unorderedInputLength == 0) && (tcp->error & TCPE_RECEIVE_EOF)) { /* there is no more unordered data either, and we need to signal EOF */ if(totalCopied > 0) { /* we just received bytes, so we can't EOF until the next call. * make sure we stay readable so we DO actually EOF the socket */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, TRUE); } else { /* OK, no more data and nothing just received. */ if(tcp->flags & TCPF_EOF_SIGNALED) { /* we already signaled close, now its an error */ return -2; } else { /* we have not signaled close, do that now and close out the socket */ _tcp_endOfFileSignalled(tcp); return 0; } } } else { /* our socket still has unordered data or is still open, but empty for now */ descriptor_adjustStatus(&(tcp->super.super.super), DS_READABLE, FALSE); } } debug("%s <-> %s: receiving %lu user bytes", tcp->super.boundString, tcp->super.peerString, totalCopied); return (gssize) (totalCopied == 0 ? -1 : totalCopied); }
static void _networkinterface_scheduleNextSend(NetworkInterface* interface) { /* the next packet needs to be sent according to bandwidth limitations. * we need to spend time sending it before sending the next. */ SimulationTime batchTime = worker_getConfig()->interfaceBatchTime; /* loop until we find a socket that has something to send */ while(interface->sendNanosecondsConsumed <= batchTime) { gint socketHandle = -1; /* choose which packet to send next based on our queuing discipline */ Packet* packet; switch(interface->qdisc) { case NIQ_RR: { packet = _networkinterface_selectRoundRobin(interface, &socketHandle); break; } case NIQ_FIFO: default: { packet = _networkinterface_selectFirstInFirstOut(interface, &socketHandle); break; } } if(!packet) { break; } packet_addDeliveryStatus(packet, PDS_SND_INTERFACE_SENT); /* now actually send the packet somewhere */ if(networkinterface_getIPAddress(interface) == packet_getDestinationIP(packet)) { /* packet will arrive on our own interface */ PacketArrivedEvent* event = packetarrived_new(packet); /* event destination is our node */ worker_scheduleEvent((Event*)event, 1, 0); } else { /* let the worker schedule with appropriate delays */ worker_schedulePacket(packet); } /* successfully sent, calculate how long it took to 'send' this packet */ guint length = packet_getPayloadLength(packet) + packet_getHeaderSize(packet); interface->sendNanosecondsConsumed += (length * interface->timePerByteUp); tracker_addOutputBytes(host_getTracker(worker_getCurrentHost()),(guint64)length, socketHandle); _networkinterface_pcapWritePacket(interface, packet); /* sending side is done with its ref */ packet_unref(packet); } /* * we need to call back and try to send more, even if we didnt consume all * of our batch time, because we might have more packets to send then. */ SimulationTime sendTime = (SimulationTime) floor(interface->sendNanosecondsConsumed); if(sendTime >= SIMTIME_ONE_NANOSECOND) { /* we are 'sending' the packets */ interface->flags |= NIF_SENDING; /* call back when the packets are 'sent' */ InterfaceSentEvent* event = interfacesent_new(interface); /* event destination is our node */ worker_scheduleEvent((Event*)event, sendTime, 0); } }