void tracker_heartbeat(Tracker* tracker) { MAGIC_ASSERT(tracker); /* prefer our level over the global config */ GLogLevelFlags level = tracker->loglevel; if(!level) { Worker* w = worker_getPrivate(); if(w->cached_engine) { Configuration* c = engine_getConfig(w->cached_engine); level = configuration_getHeartbeatLogLevel(c); } } /* prefer our interval over the global config */ SimulationTime interval = tracker->interval; if(!interval) { Worker* w = worker_getPrivate(); if(w->cached_engine) { Configuration* c = engine_getConfig(w->cached_engine); interval = configuration_getHearbeatInterval(c); } } guint seconds = (guint) (interval / SIMTIME_ONE_SECOND); double in = (double) (tracker->inputBytesLastInterval); double out = (double)(tracker->outputBytesLastInterval); double alloc = (double)(((double)tracker->allocatedBytesLastInterval) / 1024.0); double dealloc = (double)(((double)tracker->deallocatedBytesLastInterval) / 1024.0); double mem = (double)(((double)tracker->allocatedBytesTotal) / 1024.0); double cpuutil = (double)(((double)tracker->processingTimeLastInterval) / interval); double avedelayms = 0.0; if(tracker->numDelayedLastInterval > 0) { double delayms = (double) (((double)tracker->delayTimeLastInterval) / ((double)SIMTIME_ONE_MILLISECOND)); avedelayms = (double) (delayms / ((double) tracker->numDelayedLastInterval)); } /* log the things we are tracking */ logging_log(G_LOG_DOMAIN, level, __FUNCTION__, "[shadow-heartbeat] CPU %f \%, MEM %f KiB, interval %u seconds, alloc %f KiB, dealloc %f KiB, Rx %f B, Tx %f B, avgdelay %f milliseconds", cpuutil, mem, seconds, alloc, dealloc, in, out, avedelayms); /* clear interval stats */ tracker->processingTimeLastInterval = 0; tracker->delayTimeLastInterval = 0; tracker->numDelayedLastInterval = 0; tracker->inputBytesLastInterval = 0; tracker->outputBytesLastInterval = 0; tracker->allocatedBytesLastInterval = 0; tracker->deallocatedBytesLastInterval = 0; /* schedule the next heartbeat */ tracker->lastHeartbeat = worker_getPrivate()->clock_now; HeartbeatEvent* heartbeat = heartbeat_new(tracker); worker_scheduleEvent((Event*)heartbeat, interval, 0); }
Epoll* epoll_new(gint handle) { g_assert(handle >= MIN_DESCRIPTOR); Epoll* epoll = g_new0(Epoll, 1); MAGIC_INIT(epoll); descriptor_init(&(epoll->super), DT_EPOLL, &epollFunctions, handle); /* allocate backend needed for managing events for this descriptor */ epoll->watches = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, _epollwatch_free); epoll->reports = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL); /* the application may want us to watch some system files, so we need a * real OS epoll fd so we can offload that task. */ epoll->osEpollDescriptor = epoll_create(1000); if(epoll->osEpollDescriptor == -1) { warning("error in epoll_create for OS events, errno=%i", errno); } /* keep track of which virtual application we need to notify of events */ Worker* worker = worker_getPrivate(); /* epoll_new should be called as a result of an application syscall */ g_assert(worker->cached_application); epoll->ownerApplication = worker->cached_application; return epoll; }
time_t system_time(time_t* t) { time_t secs = (time_t) (worker_getPrivate()->clock_now / SIMTIME_ONE_SECOND); if(t != NULL){ *t = secs; } return secs; }
static Node* _system_switchInShadowContext() { Worker* worker = worker_getPrivate(); if(worker->cached_plugin) { plugin_setShadowContext(worker->cached_plugin, TRUE); } return worker->cached_node; }
gpointer worker_run(GSList* nodes) { /* get current thread's private worker object */ Worker* worker = worker_getPrivate(); /* continuously run all events for this worker's assigned nodes. * the simulation is done when the engine is killed. */ while(!engine_isKilled(worker->cached_engine)) { SimulationTime barrier = engine_getExecutionBarrier(worker->cached_engine); guint nEventsProcessed = 0; guint nNodesWithEvents = 0; GSList* item = nodes; while(item) { Node* node = item->data; guint n = _worker_processNode(worker, node, barrier); nEventsProcessed += n; if(n > 0) { nNodesWithEvents++; } item = g_slist_next(item); } engine_notifyProcessed(worker->cached_engine, nEventsProcessed, nNodesWithEvents); } /* free all applications before freeing any of the nodes since freeing * applications may cause close() to get called on sockets which needs * other node information. */ g_slist_foreach(nodes, (GFunc) node_stopAllApplications, NULL); g_slist_foreach(nodes, (GFunc) node_free, NULL); g_thread_exit(NULL); return NULL; }
void application_boot(Application* application) { MAGIC_ASSERT(application); Worker* worker = worker_getPrivate(); /* get arguments from the configured software */ gchar** argv; gint argc = _application_getArguments(application, &argv); /* we will need to free each argument, copy argc in case they change it */ gint n = argc; /* need to get thread-private plugin from current worker */ Plugin* plugin = worker_getPlugin(application->pluginID, application->pluginPath); worker->cached_application = application; /* create our default state as we run in our assigned worker */ application->state = plugin_newDefaultState(plugin); plugin_executeNew(plugin, application->state, argc, argv); worker->cached_application = NULL; /* free the arguments */ for(gint i = 0; i < n; i++) { g_free(argv[i]); } g_free(argv); }
gint system_getTimeOfDay(struct timeval *tv) { SimulationTime now = worker_getPrivate()->clock_now; if(tv) { tv->tv_sec = now / SIMTIME_ONE_SECOND; tv->tv_usec = (now % SIMTIME_ONE_SECOND) / SIMTIME_ONE_MICROSECOND; } return 0; }
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); g_assert(packet); /* free up buffer space */ guint length = packet_getPayloadLength(packet) + packet_getHeaderSize(packet); interface->inBufferLength -= length; /* 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)); gchar* packetString = packet_getString(packet); debug("packet in: %s", packetString); g_free(packetString); _networkinterface_pcapWritePacket(interface, packet); /* if the socket closed, just drop the packet */ gint socketHandle = -1; if(socket) { socketHandle = *descriptor_getHandleReference((Descriptor*)socket); gboolean needsRetransmit = socket_pushInPacket(socket, packet); if(needsRetransmit) { /* socket can not handle it now, so drop it */ _networkinterface_dropInboundPacket(interface, packet); } } /* successfully received, calculate how long it took to 'receive' this packet */ interface->receiveNanosecondsConsumed += (length * interface->timePerByteDown); tracker_addInputBytes(node_getTracker(worker_getPrivate()->cached_node),(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 worker_scheduleEvent(Event* event, SimulationTime nano_delay, GQuark receiver_node_id) { /* TODO create accessors, or better yet refactor the work to event class */ MAGIC_ASSERT(event); MAGIC_ASSERT((&(event->super))); /* get our thread-private worker */ Worker* worker = worker_getPrivate(); Engine* engine = worker->cached_engine; /* when the event will execute */ event->time = worker->clock_now + nano_delay; /* parties involved. sender may be NULL, receiver may not! */ Node* sender = worker->cached_node; /* we MAY NOT OWN the receiver, so do not write to it! */ Node* receiver = receiver_node_id == 0 ? sender : internetwork_getNode(worker_getInternet(), receiver_node_id); g_assert(receiver); /* the NodeEvent needs a pointer to the correct node */ event->node = receiver; /* if we are not going to execute any more events, free it and return */ if(engine_isKilled(engine)) { shadowevent_free(event); return; } /* engine is not killed, assert accurate worker clock */ g_assert(worker->clock_now != SIMTIME_INVALID); /* non-local events must be properly delayed */ SimulationTime jump = engine_getMinTimeJump(engine); if(!node_isEqual(receiver, sender)) { SimulationTime minTime = worker->clock_now + jump; /* warn and adjust time if needed */ if(event->time < minTime) { debug("Inter-node event time %lu changed to %lu due to minimum delay %lu", event->time, minTime, jump); event->time = minTime; } } /* figure out where to push the event */ if(engine_getNumThreads(engine) > 1) { /* multi-threaded, push event to receiver node */ EventQueue* eventq = node_getEvents(receiver); eventqueue_push(eventq, event); } else { /* single-threaded, push to master queue */ engine_pushEvent(engine, (Event*)event); } }
gint system_clockGetTime(clockid_t clk_id, struct timespec *tp) { if(tp == NULL) { errno = EFAULT; return -1; } SimulationTime now = worker_getPrivate()->clock_now; tp->tv_sec = now / SIMTIME_ONE_SECOND; tp->tv_nsec = now % SIMTIME_ONE_SECOND; return 0; }
void application_notify(Application* application) { MAGIC_ASSERT(application); Worker* worker = worker_getPrivate(); /* need to get thread-private plugin from current worker */ Plugin* plugin = worker_getPlugin(application->pluginID, application->pluginPath); worker->cached_application = application; plugin_executeNotify(plugin, application->state); worker->cached_application = NULL; }
/* * 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; /* create the UDP packet */ Packet* packet = packet_new(buffer + offset, copyLength); packet_setUDP(packet, PUDP_NONE, socket_getBinding(&(udp->super)), udp->super.boundPort, destinationIP, destinationPort); /* 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 = node_getTracker(worker_getPrivate()->cached_node); 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 _application_callbackTimerExpired(Application* application, ApplicationCallbackData* data) { MAGIC_ASSERT(application); g_assert(data); Worker* worker = worker_getPrivate(); /* need to get thread-private plugin from current worker */ Plugin* plugin = worker_getPlugin(application->pluginID, application->pluginPath); worker->cached_application = application; plugin_executeGeneric(plugin, application->state, data->callback, data->data, data->argument); worker->cached_application = NULL; g_free(data); }
gboolean worker_isInShadowContext() { /* this must return TRUE while destroying the thread pool to avoid * calling worker_getPrivate (which messes with threads) while trying to * shutdown the threads. */ if(shadow_engine && !(engine_isForced(shadow_engine))) { if(g_static_private_get(engine_getPreloadKey(shadow_engine))) { Worker* worker = worker_getPrivate(); if(worker->cached_plugin) { return plugin_isShadowContext(worker->cached_plugin); } } } /* if there is no engine or cached plugin, we are definitely in Shadow context */ return TRUE; }
Packet* packet_new(gconstpointer payload, gsize payloadLength) { Packet* packet = g_new0(Packet, 1); MAGIC_INIT(packet); g_mutex_init(&(packet->lock)); packet->referenceCount = 1; packet->payloadLength = payloadLength; if(payloadLength > 0) { /* if length is 0, this returns NULL */ packet->payload = g_memdup(payload, payloadLength); /* application data needs a priority ordering for FIFO onto the wire */ packet->priority = node_getNextPacketPriority(worker_getPrivate()->cached_node); } return packet; }
Plugin* worker_getPlugin(GQuark pluginID, GString* pluginPath) { g_assert(pluginPath); /* worker has a private plug-in for each plugin ID */ Worker* worker = worker_getPrivate(); Plugin* plugin = g_hash_table_lookup(worker->plugins, &pluginID); if(!plugin) { /* plug-in has yet to be loaded by this worker. do that now. this call * will copy the plug-in library to the temporary directory, and open * that so each thread can execute in its own memory space. */ plugin = plugin_new(pluginID, pluginPath); g_hash_table_replace(worker->plugins, plugin_getID(plugin), plugin); } debug("worker %i using plug-in at %p", worker->thread_id, plugin); return plugin; }
void networkinterface_sent(NetworkInterface* interface) { MAGIC_ASSERT(interface); /* we just finished sending some packets */ interface->flags &= ~NIF_SENDING; /* decide how much delay we get to absorb based on the passed time */ SimulationTime now = worker_getPrivate()->clock_now; SimulationTime absorbInterval = now - interface->lastTimeSent; if(absorbInterval > 0) { gdouble newConsumed = interface->sendNanosecondsConsumed - absorbInterval; interface->sendNanosecondsConsumed = MAX(0, newConsumed); } interface->lastTimeSent = now; /* now try to send the next ones */ _networkinterface_scheduleNextSend(interface); }
void networkinterface_received(NetworkInterface* interface) { MAGIC_ASSERT(interface); /* we just finished receiving some packets */ interface->flags &= ~NIF_RECEIVING; /* decide how much delay we get to absorb based on the passed time */ SimulationTime now = worker_getPrivate()->clock_now; SimulationTime absorbInterval = now - interface->lastTimeReceived; if(absorbInterval > 0) { /* decide how much delay we get to absorb based on the passed time */ gdouble newConsumed = interface->receiveNanosecondsConsumed - absorbInterval; interface->receiveNanosecondsConsumed = MAX(0, newConsumed); } interface->lastTimeReceived = now; /* now try to receive the next ones */ _networkinterface_scheduleNextReceive(interface); }
static gint _engine_processEvents(Engine* engine) { MAGIC_ASSERT(engine); Event* next_event = asyncpriorityqueue_peek(engine->masterEventQueue); if(next_event) { Worker* worker = worker_getPrivate(); worker->clock_now = SIMTIME_INVALID; worker->clock_last = 0; worker->cached_engine = engine; /* process all events in the priority queue */ while(next_event && (next_event->time < engine->executeWindowEnd) && (next_event->time < engine->endTime)) { /* get next event */ next_event = asyncpriorityqueue_pop(engine->masterEventQueue); worker->cached_event = next_event; MAGIC_ASSERT(worker->cached_event); worker->cached_node = next_event->node; /* ensure priority */ worker->clock_now = worker->cached_event->time; engine->clock = worker->clock_now; g_assert(worker->clock_now >= worker->clock_last); gboolean complete = shadowevent_run(worker->cached_event); if(complete) { shadowevent_free(worker->cached_event); } worker->cached_event = NULL; worker->cached_node = NULL; worker->clock_last = worker->clock_now; worker->clock_now = SIMTIME_INVALID; next_event = asyncpriorityqueue_peek(engine->masterEventQueue); } } return 0; }
static Packet* _tcp_createPacket(TCP* tcp, enum ProtocolTCPFlags flags, gconstpointer payload, gsize payloadLength) { MAGIC_ASSERT(tcp); /* * packets from children of a server must appear to be coming from the server */ in_addr_t sourceIP = tcp_getIP(tcp); in_port_t sourcePort = (tcp->child) ? tcp->child->parent->super.boundPort : tcp->super.boundPort; in_addr_t destinationIP = tcp_getPeerIP(tcp); in_port_t destinationPort = (tcp->server) ? tcp->server->lastPeerPort : tcp->super.peerPort; if(sourceIP == htonl(INADDR_ANY)) { sourceIP = node_getDefaultIP(worker_getPrivate()->cached_node); } g_assert(sourceIP && sourcePort && destinationIP && destinationPort); /* make sure our receive window is up to date before putting it in the packet */ _tcp_updateReceiveWindow(tcp); /* control packets have no sequence number * (except FIN, so we close after sending everything) */ guint sequence = ((payloadLength > 0) || (flags & PTCP_FIN)) ? tcp->send.next : 0; /* create the TCP packet */ Packet* packet = packet_new(payload, payloadLength); packet_setTCP(packet, flags, sourceIP, sourcePort, destinationIP, destinationPort, sequence, tcp->receive.next, tcp->receive.window); /* update sequence number */ if(sequence > 0) { tcp->send.next++; } return packet; }
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); g_assert(bytesCopied == copyLength); /* 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 = node_getTracker(worker_getPrivate()->cached_node); 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; }
CPU* cpu_new(guint frequencyKHz, gint threshold, gint precision) { CPU* cpu = g_new0(CPU, 1); MAGIC_INIT(cpu); cpu->frequencyKHz = frequencyKHz; cpu->threshold = threshold > 0 ? (threshold * SIMTIME_ONE_MICROSECOND) : SIMTIME_INVALID; cpu->precision = precision > 0 ? (precision * SIMTIME_ONE_MICROSECOND) : SIMTIME_INVALID; cpu->timeCPUAvailable = cpu->now = 0; /* get the raw speed of the experiment machine */ guint rawFrequencyKHz = engine_getRawCPUFrequency(worker_getPrivate()->cached_engine); if(!rawFrequencyKHz) { warning("unable to determine raw CPU frequency, setting %i KHz as a raw " "estimate, and using delay ratio of 1.0 to the simulator host", cpu->frequencyKHz); cpu->rawFrequencyKHz = cpu->frequencyKHz; cpu->frequencyRatio = 1.0; } else { cpu->rawFrequencyKHz = rawFrequencyKHz; cpu->frequencyRatio = (gdouble)((gdouble)cpu->rawFrequencyKHz) / ((gdouble)cpu->frequencyKHz); } return cpu; }
static Channel* channel_getLinkedChannel(Channel* channel) { MAGIC_ASSERT(channel); return (Channel*)node_lookupDescriptor(worker_getPrivate()->cached_node, channel->linkedHandle); }
/* needed for multi-threaded openssl * @see '$man CRYPTO_lock' */ unsigned long system_cryptoIdFunc() { Worker* worker = worker_getPrivate(); return ((unsigned long) (worker->thread_id)); }
/* needed for multi-threaded openssl * @see '$man CRYPTO_lock' */ void system_cryptoLockingFunc(int mode, int n, const char *file, int line) { Worker* worker = worker_getPrivate(); engine_cryptoLockingFunc(worker->cached_engine, mode, n); }
static void _system_switchOutShadowContext(Node* node) { Worker* worker = worker_getPrivate(); if(worker->cached_plugin) { plugin_setShadowContext(worker->cached_plugin, FALSE); } }
void udp_close(UDP* udp) { MAGIC_ASSERT(udp); node_closeDescriptor(worker_getPrivate()->cached_node, udp->super.super.super.handle); }
/* 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; }
static void _tcp_setState(TCP* tcp, enum TCPState state) { MAGIC_ASSERT(tcp); tcp->stateLast = tcp->state; tcp->state = state; debug("%s <-> %s: moved from TCP state '%s' to '%s'", tcp->super.boundString, tcp->super.peerString, tcp_stateToAscii(tcp->stateLast), tcp_stateToAscii(tcp->state)); /* some state transitions require us to update the descriptor status */ switch (state) { case TCPS_LISTEN: { descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, TRUE); break; } case TCPS_SYNSENT: { break; } case TCPS_SYNRECEIVED: { break; } case TCPS_ESTABLISHED: { tcp->flags |= TCPF_WAS_ESTABLISHED; if(tcp->state != tcp->stateLast) { _tcp_autotune(tcp); } descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE|DS_WRITABLE, TRUE); break; } case TCPS_CLOSING: { break; } case TCPS_CLOSEWAIT: { break; } case TCPS_CLOSED: { /* user can no longer use socket */ descriptor_adjustStatus((Descriptor*)tcp, DS_ACTIVE, FALSE); /* * servers have to wait for all children to close. * children need to notify their parents when closing. */ if(!tcp->server || g_hash_table_size(tcp->server->children) <= 0) { if(tcp->child && tcp->child->parent) { TCP* parent = tcp->child->parent; /* tell my server to stop accepting packets for me * this will destroy the child and NULL out tcp->child */ g_hash_table_remove(tcp->child->parent->server->children, (gconstpointer)&(tcp->child->key)); /* if i was the server's last child and its waiting to close, close it */ g_assert(parent->server); if((parent->state == TCPS_CLOSED) && (g_hash_table_size(parent->server->children) <= 0)) { /* this will unbind from the network interface and free socket */ node_closeDescriptor(worker_getPrivate()->cached_node, parent->super.super.super.handle); } } /* this will unbind from the network interface and free socket */ node_closeDescriptor(worker_getPrivate()->cached_node, tcp->super.super.super.handle); } break; } case TCPS_TIMEWAIT: { /* schedule a close timer self-event to finish out the closing process */ TCPCloseTimerExpiredEvent* event = tcpclosetimerexpired_new(tcp); worker_scheduleEvent((Event*)event, CONFIG_TCPCLOSETIMER_DELAY, 0); break; } default: break; } }
static void channel_close(Channel* channel) { MAGIC_ASSERT(channel); descriptor_adjustStatus((Descriptor*)channel, DS_CLOSED, TRUE); node_closeDescriptor(worker_getPrivate()->cached_node, channel->super.super.handle); }