コード例 #1
0
ファイル: shd-tcp.c プロジェクト: icbaker/shadow
void tcp_droppedPacket(TCP* tcp, Packet* packet) {
	MAGIC_ASSERT(tcp);

	PacketTCPHeader header;
	packet_getTCPHeader(packet, &header);

	/* if we run a server, the packet could be for an existing child */
	tcp = _tcp_getSourceTCP(tcp, header.destinationIP, header.destinationPort);

	/* if we are closed, we don't care */
	if(tcp->state == TCPS_CLOSED) {
		return;
	}

	/* the packet was "dropped" - this is basically a negative ack.
	 * handle congestion control.
	 * TCP-Reno-like fast retransmit, i.e. multiplicative decrease. */
	tcp->congestion.window /= 2;
	if(tcp->congestion.window < 1) {
		tcp->congestion.window = 1;
	}
	if(tcp->isSlowStart && tcp->congestion.threshold == 0) {
		tcp->congestion.threshold = (guint32) tcp->congestion.window;
	}

	debug("%s <-> %s: retransmitting packet# %u", tcp->super.boundString, tcp->super.peerString, header.sequence);

	/* buffer and send as appropriate */
	_tcp_removeRetransmit(tcp, header.sequence);
	_tcp_bufferPacketOut(tcp, packet);
	_tcp_flush(tcp);
}
コード例 #2
0
ファイル: shd-pcap-writer.c プロジェクト: 4sp1r3/shadow
void pcapwriter_writePacket(PCapWriter* pcap, Packet* packet) {
    if(!pcap || !pcap->pcapFile || !packet) {
        return;
    }

    guint32 ts_sec;         /* timestamp seconds */
    guint32 ts_usec;        /* timestamp microseconds */
    guint32 incl_len;       /* number of octets of packet saved in file */
    guint32 orig_len;       /* actual length of packet */

    /* get the current time that the packet is being sent/received */
    SimulationTime now = worker_getCurrentTime();
    ts_sec = now / SIMTIME_ONE_SECOND;
    ts_usec = (now % SIMTIME_ONE_SECOND) / SIMTIME_ONE_MICROSECOND;

    /* get the header and payload lengths */
    guint headerSize = packet_getHeaderSize(packet);
    guint payloadLength = packet_getPayloadLength(packet);
    incl_len = headerSize + payloadLength;
    orig_len = headerSize + payloadLength;

    /* get the TCP header and the payload */
    PacketTCPHeader tcpHeader;
    guchar *payload = g_new0(guchar, payloadLength);
    packet_getTCPHeader(packet, &tcpHeader);
    packet_copyPayload(packet, 0, payload, payloadLength);

    /* write the PCAP packet header to the pcap file */
    fwrite(&ts_sec, sizeof(ts_sec), 1, pcap->pcapFile);
    fwrite(&ts_usec, sizeof(ts_usec), 1, pcap->pcapFile);
    fwrite(&incl_len, sizeof(incl_len), 1, pcap->pcapFile);
    fwrite(&orig_len, sizeof(orig_len), 1, pcap->pcapFile);

    /* write the ethernet header */
    guint8 destinationMAC[6] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB};
    guint8 sourceMAC[6] = {0xA1, 0xB2, 0xC3, 0xD4, 0xE5, 0xF6};
    guint16 type = htons(0x0800);

    fwrite(destinationMAC, 1, sizeof(destinationMAC), pcap->pcapFile);
    fwrite(sourceMAC, 1, sizeof(sourceMAC), pcap->pcapFile);
    fwrite(&type, 1, sizeof(type), pcap->pcapFile);

    /* write the IP header */
    guint8 versionAndHeaderLength = 0x45;
    guint8 fields = 0x00;
    guint16 totalLength = htons(orig_len - 14);
    guint16 identification = 0x0000;
    guint16 flagsAndFragment = 0x0040;
    guint8 timeToLive = 64;
    guint8 protocol = 6;  /* TCP */
    guint16 headerChecksum = 0x0000;
    guint32 sourceIP = tcpHeader.sourceIP;
    guint32 destinationIP = tcpHeader.destinationIP;

    fwrite(&versionAndHeaderLength, 1, sizeof(versionAndHeaderLength), pcap->pcapFile);
    fwrite(&fields, 1, sizeof(fields), pcap->pcapFile);
    fwrite(&totalLength, 1, sizeof(totalLength), pcap->pcapFile);
    fwrite(&identification, 1, sizeof(identification), pcap->pcapFile);
    fwrite(&flagsAndFragment, 1, sizeof(flagsAndFragment), pcap->pcapFile);
    fwrite(&timeToLive, 1, sizeof(timeToLive), pcap->pcapFile);
    fwrite(&protocol, 1, sizeof(protocol), pcap->pcapFile);
    fwrite(&headerChecksum, 1, sizeof(headerChecksum), pcap->pcapFile);
    fwrite(&sourceIP, 1, sizeof(sourceIP), pcap->pcapFile);
    fwrite(&destinationIP, 1, sizeof(destinationIP), pcap->pcapFile);


    /* write the TCP header */
    guint16 sourcePort = tcpHeader.sourcePort;
    guint16 destinationPort = tcpHeader.destinationPort;
    guint32 sequence = tcpHeader.sequence;
    guint32 acknowledgement = 0;
    if(tcpHeader.flags & PTCP_ACK) {
        acknowledgement = htonl(tcpHeader.acknowledgment);
    }
    guint8 headerLength = 0x80;
    guint8 tcpFlags = 0;
    if(tcpHeader.flags & PTCP_RST) tcpFlags |= 0x04;
    if(tcpHeader.flags & PTCP_SYN) tcpFlags |= 0x02;
    if(tcpHeader.flags & PTCP_ACK) tcpFlags |= 0x10;
    if(tcpHeader.flags & PTCP_FIN) tcpFlags |= 0x01;
    guint16 window = tcpHeader.window;
    guint16 tcpChecksum = 0x0000;
    guint8 options[14] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    fwrite(&sourcePort, 1, sizeof(sourcePort), pcap->pcapFile);
    fwrite(&destinationPort, 1, sizeof(destinationPort), pcap->pcapFile);
    fwrite(&sequence, 1, sizeof(sequence), pcap->pcapFile);
    fwrite(&acknowledgement, 1, sizeof(acknowledgement), pcap->pcapFile);
    fwrite(&headerLength, 1, sizeof(headerLength), pcap->pcapFile);
    fwrite(&tcpFlags, 1, sizeof(tcpFlags), pcap->pcapFile);
    fwrite(&window, 1, sizeof(window), pcap->pcapFile);
    fwrite(&tcpChecksum, 1, sizeof(tcpChecksum), pcap->pcapFile);
    fwrite(options, 1, sizeof(options), pcap->pcapFile);

    /* write payload data */
    if(payloadLength > 0) {
        fwrite(payload, 1, payloadLength, pcap->pcapFile);
    }

    g_free(payload);
}
コード例 #3
0
ファイル: shd-tcp.c プロジェクト: icbaker/shadow
/* 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;
}
コード例 #4
0
ファイル: shd-tcp.c プロジェクト: icbaker/shadow
static void _tcp_flush(TCP* tcp) {
	MAGIC_ASSERT(tcp);

	/* make sure our information is up to date */
	_tcp_updateReceiveWindow(tcp);
	_tcp_updateSendWindow(tcp);

	/* flush packets that can now be sent to socket */
	while(g_queue_get_length(tcp->throttledOutput) > 0) {
		/* get the next throttled packet, in sequence order */
		Packet* packet = g_queue_pop_head(tcp->throttledOutput);

		/* break out if we have no packets left */
		if(!packet) {
			break;
		}

		guint length = packet_getPayloadLength(packet);

		if(length > 0) {
			PacketTCPHeader header;
			packet_getTCPHeader(packet, &header);

			/* we cant send it if our window is too small */
			gboolean fitsInWindow = (header.sequence < (tcp->send.unacked + tcp->send.window)) ? TRUE : FALSE;

			/* we cant send it if we dont have enough space */
			gboolean fitsInBuffer = (length <= socket_getOutputBufferSpace(&(tcp->super))) ? TRUE : FALSE;

			if(!fitsInBuffer || !fitsInWindow) {
				/* we cant send the packet yet */
				g_queue_push_head(tcp->throttledOutput, packet);
				break;
			} else {
				/* we will send: store length in virtual retransmission buffer
				 * so we can reduce buffer space consumed when we receive the ack */
				_tcp_addRetransmit(tcp, header.sequence, length);
			}
		}

		/* packet is sendable, we removed it from out buffer */
		tcp->throttledOutputLength -= length;

		/* update TCP header to our current advertised window and acknowledgement */
		packet_updateTCP(packet, tcp->receive.next, tcp->receive.window);

		/* keep track of the last things we sent them */
		tcp->send.lastAcknowledgement = tcp->receive.next;
		tcp->send.lastWindow = tcp->receive.window;

		 /* socket will queue it ASAP */
		gboolean success = socket_addToOutputBuffer(&(tcp->super), packet);

		/* we already checked for space, so this should always succeed */
		g_assert(success);
	}

	/* any packets now in order can be pushed to our user input buffer */
	while(g_queue_get_length(tcp->unorderedInput) > 0) {
		Packet* packet = g_queue_pop_head(tcp->unorderedInput);

		PacketTCPHeader header;
		packet_getTCPHeader(packet, &header);

		if(header.sequence == tcp->receive.next) {
			/* move from the unordered buffer to user input buffer */
			gboolean fitInBuffer = socket_addToInputBuffer(&(tcp->super), packet);

			if(fitInBuffer) {
				tcp->unorderedInputLength -= packet_getPayloadLength(packet);
				(tcp->receive.next)++;
				continue;
			}
		}

		/* we could not buffer it because its out of order or we have no space */
		g_queue_push_head(tcp->unorderedInput, packet);
		break;
	}

	/* check if user needs an EOF signal */
	gboolean wantsEOF = ((tcp->flags & TCPF_LOCAL_CLOSED) || (tcp->flags & TCPF_REMOTE_CLOSED)) ? TRUE : FALSE;
	if(wantsEOF) {
		/* if anyone closed, can't send anymore */
		tcp->error |= TCPE_SEND_EOF;

		if((tcp->receive.next >= tcp->receive.end) && !(tcp->flags & TCPF_EOF_SIGNALED)) {
			/* user needs to read a 0 so it knows we closed */
			tcp->error |= TCPE_RECEIVE_EOF;
			descriptor_adjustStatus((Descriptor*)tcp, DS_READABLE, TRUE);
		}
	}
}