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; }
void host_returnHandleHack(gint handle) { /* TODO replace this with something more graceful? */ Host* host = worker_getCurrentHost(); if(host) { _host_returnPreviousDescriptorHandle(host, handle); } }
static Host* _system_switchInShadowContext() { Plugin* plugin = worker_getCurrentPlugin(); if(plugin) { plugin_setShadowContext(plugin, TRUE); } return worker_getCurrentHost(); }
gboolean worker_isFiltered(GLogLevelFlags level) { Worker* worker = worker_isAlive() ? _worker_getPrivate() : NULL; if(worker) { /* check the local node log level first */ gboolean isNodeLevelSet = FALSE; Host* currentHost = worker_getCurrentHost(); if(worker->cached_node) { GLogLevelFlags nodeLevel = host_getLogLevel(currentHost); if(nodeLevel) { isNodeLevelSet = TRUE; if(level > nodeLevel) { return TRUE; } } } /* only check the global config if the node didnt have a local setting */ if(!isNodeLevelSet) { Configuration* c = slave_getConfig(worker->slave); if(c && (level > configuration_getLogLevel(c))) { return TRUE; } } } return FALSE; }
Packet* socket_removeFromOutputBuffer(Socket* socket) { MAGIC_ASSERT(socket); /* see if we have any packets */ Packet* packet = g_queue_pop_head(socket->outputBuffer); if(packet) { /* just removed a packet */ guint length = packet_getPayloadLength(packet); socket->outputBufferLength -= length; /* check if we need to reduce the buffer size */ if(socket->outputBufferSizePending > 0) { socket_setOutputBufferSize(socket, socket->outputBufferSizePending); } /* 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 are writable if we now have space */ if(socket_getOutputBufferSpace(socket) > 0) { descriptor_adjustStatus((Descriptor*)socket, DS_WRITABLE, TRUE); } } return packet; }
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; }
PCapWriter* pcapwriter_new(gchar* pcapDirectory, gchar* pcapFilename) { PCapWriter* pcap = g_new0(PCapWriter, 1); /* open the PCAP file for writing */ GString *filename = g_string_new(""); if (pcapDirectory) { g_string_append(filename, pcapDirectory); /* Append trailing slash if not present */ if (!g_str_has_suffix(pcapDirectory, "/")) { g_string_append(filename, "/"); } } else { /* Use default directory */ g_string_append(filename, "data/pcapdata/"); } if(pcapFilename) { g_string_append_printf(filename, "%s", pcapFilename); } else { g_string_append_printf(filename, "%s", host_getName(worker_getCurrentHost())); } if (!g_str_has_suffix(filename->str, ".pcap")) { g_string_append(filename, ".pcap"); } pcap->pcapFile = fopen(filename->str, "w"); if(!pcap->pcapFile) { warning("error trying to open PCAP file '%s' for writing", filename->str); } else { _pcapwriter_writeHeader(pcap); } return pcap; }
void socket_close(Socket* socket) { MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_removeSocket(tracker, descriptor->handle); socket->vtable->close((Descriptor*)socket); }
gint socket_connectToPeer(Socket* socket, in_addr_t ip, in_port_t port, sa_family_t family) { MAGIC_ASSERT(socket); MAGIC_ASSERT(socket->vtable); Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_updateSocketPeer(tracker, descriptor->handle, ip, ntohs(port)); return socket->vtable->connectToPeer(socket, ip, port, family); }
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); } }
void socket_init(Socket* socket, SocketFunctionTable* vtable, DescriptorType type, gint handle, guint receiveBufferSize, guint sendBufferSize) { utility_assert(socket && vtable); transport_init(&(socket->super), &socket_functions, type, handle); MAGIC_INIT(socket); MAGIC_INIT(vtable); socket->vtable = vtable; socket->protocol = type == DT_TCPSOCKET ? PTCP : type == DT_UDPSOCKET ? PUDP : PLOCAL; socket->inputBuffer = g_queue_new(); socket->inputBufferSize = receiveBufferSize; socket->outputBuffer = g_queue_new(); socket->outputBufferSize = sendBufferSize; Tracker* tracker = host_getTracker(worker_getCurrentHost()); Descriptor* descriptor = (Descriptor *)socket; tracker_addSocket(tracker, descriptor->handle, socket->protocol, socket->inputBufferSize, socket->outputBufferSize); }
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); } }
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; }
/* * 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; }
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); } }
void udp_close(UDP* udp) { MAGIC_ASSERT(udp); host_closeDescriptor(worker_getCurrentHost(), udp->super.super.super.handle); }
void logging_logv(const gchar *msgLogDomain, GLogLevelFlags msgLogLevel, const gchar* fileName, const gchar* functionName, const gint lineNumber, const gchar *format, va_list vargs) { /* this is called by worker threads, so we have access to worker */ /* see if we can avoid some work because the message is filtered anyway */ const gchar* logDomainStr = msgLogDomain ? msgLogDomain : "shadow"; if(worker_isFiltered(msgLogLevel)) { return; } gchar* logFileStr = fileName ? g_path_get_basename(fileName) : g_strdup("n/a"); const gchar* logFunctionStr = functionName ? functionName : "n/a"; const gchar* formatStr = format ? format : "n/a"; const gchar* logLevelStr = _logging_getNewLogLevelString(msgLogLevel); SimulationTime currentTime = worker_isAlive() ? worker_getCurrentTime() : SIMTIME_INVALID; Host* currentHost = worker_isAlive() ? worker_getCurrentHost() : NULL; gint workerThreadID = worker_isAlive() ? worker_getThreadID() : 0; /* format the simulation time if we are running an event */ GString* clockStringBuffer = g_string_new(""); if(currentTime != SIMTIME_INVALID) { SimulationTime hours, minutes, seconds, remainder; remainder = currentTime; hours = remainder / SIMTIME_ONE_HOUR; remainder %= SIMTIME_ONE_HOUR; minutes = remainder / SIMTIME_ONE_MINUTE; remainder %= SIMTIME_ONE_MINUTE; seconds = remainder / SIMTIME_ONE_SECOND; remainder %= SIMTIME_ONE_SECOND; g_string_printf(clockStringBuffer, "%02"G_GUINT64_FORMAT":%02"G_GUINT64_FORMAT":%02"G_GUINT64_FORMAT".%09"G_GUINT64_FORMAT"", hours, minutes, seconds, remainder); } else { g_string_printf(clockStringBuffer, "n/a"); } /* we'll need to free clockString later */ gchar* clockString = g_string_free(clockStringBuffer, FALSE); /* node identifier, if we are running a node * dont free this since we dont own the ip address string */ GString* nodeStringBuffer = g_string_new(""); if(currentHost) { g_string_printf(nodeStringBuffer, "%s~%s", host_getName(currentHost), host_getDefaultIPName(currentHost)); } else { g_string_printf(nodeStringBuffer, "n/a"); } gchar* nodeString = g_string_free(nodeStringBuffer, FALSE); /* the function name - no need to free this */ GString* newLogFormatBuffer = g_string_new(NULL); g_string_printf(newLogFormatBuffer, "[thread-%i] %s [%s-%s] [%s] [%s:%i] [%s] %s", workerThreadID, clockString, logDomainStr, logLevelStr, nodeString, logFileStr, lineNumber, logFunctionStr, formatStr); /* get the new format out of our string buffer and log it */ gchar* newLogFormat = g_string_free(newLogFormatBuffer, FALSE); g_logv(logDomainStr, msgLogLevel, newLogFormat, vargs); /* cleanup */ g_free(logFileStr); g_free(newLogFormat); g_free(clockString); g_free(nodeString); }