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); } }
gboolean socket_addToOutputBuffer(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); /* check if the packet fits */ guint length = packet_getPayloadLength(packet); if(length > socket_getOutputBufferSpace(socket)) { return FALSE; } /* add to our queue */ g_queue_push_tail(socket->outputBuffer, packet); socket->outputBufferLength += length; packet_addDeliveryStatus(packet, PDS_SND_SOCKET_BUFFERED); /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketOutputBuffer(tracker, descriptor->handle, socket->outputBufferLength, socket->outputBufferSize); /* we just added a packet, we are no longer writable if full */ if(socket_getOutputBufferSpace(socket) <= 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, FALSE); } /* tell the interface to include us when sending out to the network */ in_addr_t ip = packet_getSourceIP(packet); NetworkInterface* interface = host_lookupInterface(worker_getCurrentHost(), ip); networkinterface_wantsSend(interface, socket); return TRUE; }
gboolean socket_addToInputBuffer(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); /* check if the packet fits */ guint length = packet_getPayloadLength(packet); if(length > socket_getInputBufferSpace(socket)) { return FALSE; } /* add to our queue */ g_queue_push_tail(socket->inputBuffer, packet); packet_ref(packet); socket->inputBufferLength += length; packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_BUFFERED); /* update the tracker input buffer stats */ Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketInputBuffer(tracker, descriptor->handle, socket->inputBufferLength, socket->inputBufferSize); /* we just added a packet, so we are readable */ if(socket->inputBufferLength > 0) { descriptor_adjustStatus((Descriptor*)socket, DS_READABLE, TRUE); } return TRUE; }
void udp_processPacket(UDP* udp, Packet* packet) { MAGIC_ASSERT(udp); /* UDP packet contains data for user and can be buffered immediately */ if(packet_getPayloadLength(packet) > 0) { if(!socket_addToInputBuffer((Socket*)udp, packet)) { packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_DROPPED); } } }
void worker_schedulePacket(Packet* packet) { /* get our thread-private worker */ Worker* worker = _worker_getPrivate(); if(slave_isKilled(worker->slave)) { /* the simulation is over, don't bother */ return; } in_addr_t srcIP = packet_getSourceIP(packet); in_addr_t dstIP = packet_getDestinationIP(packet); Address* srcAddress = dns_resolveIPToAddress(worker_getDNS(), (guint32) srcIP); Address* dstAddress = dns_resolveIPToAddress(worker_getDNS(), (guint32) dstIP); if(!srcAddress || !dstAddress) { error("unable to schedule packet because of null addresses"); return; } /* check if network reliability forces us to 'drop' the packet */ gdouble reliability = topology_getReliability(worker_getTopology(), srcAddress, dstAddress); Random* random = host_getRandom(worker_getCurrentHost()); gdouble chance = random_nextDouble(random); /* don't drop control packets with length 0, otherwise congestion * control has problems responding to packet loss */ if(chance <= reliability || packet_getPayloadLength(packet) == 0) { /* the sender's packet will make it through, find latency */ gdouble latency = topology_getLatency(worker_getTopology(), srcAddress, dstAddress); SimulationTime delay = (SimulationTime) ceil(latency * SIMTIME_ONE_MILLISECOND); PacketArrivedEvent* event = packetarrived_new(packet); worker_scheduleEvent((Event*)event, delay, (GQuark)address_getID(dstAddress)); packet_addDeliveryStatus(packet, PDS_INET_SENT); } else { packet_addDeliveryStatus(packet, PDS_INET_DROPPED); } }
void networkinterface_packetArrived(NetworkInterface* interface, Packet* packet) { MAGIC_ASSERT(interface); /* a packet arrived. lets try to receive or buffer it */ guint length = packet_getPayloadLength(packet) + packet_getHeaderSize(packet); gssize space = interface->inBufferSize - interface->inBufferLength; utility_assert(space >= 0); if(length <= space) { /* we have space to buffer it */ packet_ref(packet); g_queue_push_tail(interface->inBuffer, packet); interface->inBufferLength += length; packet_addDeliveryStatus(packet, PDS_RCV_INTERFACE_BUFFERED); /* we need a trigger if we are not currently receiving */ if(!(interface->flags & NIF_RECEIVING)) { _networkinterface_scheduleNextReceive(interface); } } else { /* buffers are full, drop packet */ packet_addDeliveryStatus(packet, PDS_RCV_INTERFACE_DROPPED); } }
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; }
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); } }
/* * this function builds a UDP packet and sends to the virtual node given by the * ip and port parameters. this function assumes that the socket is already * bound to a local port, no matter if that happened explicitly or implicitly. */ gssize udp_sendUserData(UDP* udp, gconstpointer buffer, gsize nBytes, in_addr_t ip, in_port_t port) { MAGIC_ASSERT(udp); gsize space = socket_getOutputBufferSpace(&(udp->super)); if(space < nBytes) { /* not enough space to buffer the data */ return -1; } /* break data into segments and send each in a packet */ gsize maxPacketLength = CONFIG_DATAGRAM_MAX_SIZE; gsize remaining = nBytes; gsize offset = 0; /* create as many packets as needed */ while(remaining > 0) { gsize copyLength = MIN(maxPacketLength, remaining); /* use default destination if none was specified */ in_addr_t destinationIP = (ip != 0) ? ip : udp->super.peerIP; in_port_t destinationPort = (port != 0) ? port : udp->super.peerPort; in_addr_t sourceIP = 0; in_port_t sourcePort = 0; socket_getSocketName(&(udp->super), &sourceIP, &sourcePort); if(sourceIP == htonl(INADDR_ANY)) { /* source interface depends on destination */ if(destinationIP == htonl(INADDR_LOOPBACK)) { sourceIP = htonl(INADDR_LOOPBACK); } else { sourceIP = host_getDefaultIP(worker_getCurrentHost()); } } utility_assert(sourceIP && sourcePort && destinationIP && destinationPort); /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, sourceIP, sourcePort, destinationIP, destinationPort); packet_addDeliveryStatus(packet, PDS_SND_CREATED); /* buffer it in the transport layer, to be sent out when possible */ gboolean success = socket_addToOutputBuffer((Socket*) udp, packet); /* counter maintenance */ if(success) { remaining -= copyLength; offset += copyLength; } else { warning("unable to send UDP packet"); break; } } /* 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("buffered %"G_GSIZE_FORMAT" outbound UDP bytes from user", offset); return (gssize) offset; }
void socket_pushInPacket(Socket* socket, Packet* packet) { MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); packet_addDeliveryStatus(packet, PDS_RCV_SOCKET_PROCESSED); socket->vtable->process(socket, packet); }