Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
static Node* _system_switchInShadowContext() {
	Worker* worker = worker_getPrivate();
	if(worker->cached_plugin) {
		plugin_setShadowContext(worker->cached_plugin, TRUE);
	}
	return worker->cached_node;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 7
0
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);
	}
}
Ejemplo n.º 9
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);
	}
}
Ejemplo n.º 10
0
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;
}
Ejemplo n.º 11
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;
}
Ejemplo n.º 12
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;

		/* 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;
}
Ejemplo n.º 13
0
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);
}
Ejemplo n.º 14
0
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;
}
Ejemplo n.º 15
0
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;
}
Ejemplo n.º 16
0
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);
}
Ejemplo n.º 19
0
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;
}
Ejemplo n.º 20
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;
}
Ejemplo n.º 21
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);

	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;
}
Ejemplo n.º 22
0
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;
}
Ejemplo n.º 23
0
static Channel* channel_getLinkedChannel(Channel* channel) {
	MAGIC_ASSERT(channel);
	return (Channel*)node_lookupDescriptor(worker_getPrivate()->cached_node, channel->linkedHandle);
}
Ejemplo n.º 24
0
/* needed for multi-threaded openssl
 * @see '$man CRYPTO_lock'
 */
unsigned long system_cryptoIdFunc() {
	Worker* worker = worker_getPrivate();
	return ((unsigned long) (worker->thread_id));
}
Ejemplo n.º 25
0
/* 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);
}
Ejemplo n.º 26
0
static void _system_switchOutShadowContext(Node* node) {
	Worker* worker = worker_getPrivate();
	if(worker->cached_plugin) {
		plugin_setShadowContext(worker->cached_plugin, FALSE);
	}
}
Ejemplo n.º 27
0
void udp_close(UDP* udp) {
	MAGIC_ASSERT(udp);
	node_closeDescriptor(worker_getPrivate()->cached_node, udp->super.super.super.handle);
}
Ejemplo n.º 28
0
/* 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;
}
Ejemplo n.º 29
0
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;
	}
}
Ejemplo n.º 30
0
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);
}