Livt packages

Livt.Net

Version 1.4.1

Ethernet, IPv4, TCP, UDP, ARP, TFTP, and Telnet networking components for Livt hardware.

Published files (163)

src/tcp/TcpSegment.lvt

namespace Livt.Net

/**
 * Prepared fixed-header TCP segment over a borrowed payload. No options or
 * connection state. Preparation reads the complete payload once to calculate
 * the IPv4 pseudo-header checksum. Only variable header fields are stored here.
 */
component TcpSegment<P: IPacketData> : IPacketData, IIpv4Payload
{
	// Borrowed publication and owned prepared header metadata.
	payload: P
	sourcePortWord: logic[16]
	destinationPortWord: logic[16]
	sequenceHigh: logic[16]
	sequenceLow: logic[16]
	ackHigh: logic[16]
	ackLow: logic[16]
	windowWord: logic[16]
	checksumWord: logic[16]
	flagsByte: byte
	length: logic[16]
	ready: bool
	calculator: InternetChecksum

	/** Binds stable payload storage; starts unprepared. */
	new(payload: P)
	{
		this.payload = payload
		this.calculator = new InternetChecksum()
	}

	/** Finish dependent reads/TX borrows and invalidate ancestors first. */
	public fn Invalidate()
	{
		this.ready = false
		this.length = 0
	}

	/**
	 * Arrays are network order. Use these same IP addresses for the enclosing
	 * IPv4 packet. Payload length may be 0..65495, so the complete IPv4 packet
	 * fits 65535 bytes. Failure invalidates prior metadata; read errors propagate.
	 * Keep the payload publication unchanged until all parent/TX borrows finish.
	 */
	public fn TryPrepare(sourceIp: byte[4], destinationIp: byte[4],
		sourcePort: byte[2], destinationPort: byte[2], sequence: byte[4],
		acknowledgment: byte[4], flags: byte, window: byte[2]) PacketDataResult
	{
		this.Invalidate()

		if (!this.payload.IsReady()) {
			return PacketDataResult.NotReady
		}

		var size = this.payload.GetAvailableLength()

		if (size < 0 || size > 65495) {
			return PacketDataResult.Invalid
		}

		this.calculator.Reset()

		sourcePortWord = NetworkOrder.Word(sourcePort[0], sourcePort[1]) as logic[16]
		destinationPortWord = NetworkOrder.Word(destinationPort[0], destinationPort[1]) as logic[16]
		sequenceHigh = NetworkOrder.Word(sequence[0], sequence[1]) as logic[16]
		sequenceLow = NetworkOrder.Word(sequence[2], sequence[3]) as logic[16]
		ackHigh = NetworkOrder.Word(acknowledgment[0], acknowledgment[1]) as logic[16]
		ackLow = NetworkOrder.Word(acknowledgment[2], acknowledgment[3]) as logic[16]
		windowWord = NetworkOrder.Word(window[0], window[1]) as logic[16]
		flagsByte = flags

		for (var i = 0; i < size; i++) {
			var value: byte = 0x00
			var result = this.payload.TryRead(i, value)

			if (result != PacketDataResult.Success) {
				return result
			}

			this.calculator.AddByte(value)
		}

		var payloadWordSum = this.calculator.GetWordSum()
		checksumWord = TcpChecksum.CalculatePayloadChecksum(sourceIp, destinationIp,
			sourcePort, destinationPort, sequence, acknowledgment, flags, window,
			20 + size, payloadWordSum) as logic[16]
		this.length = (20 + size) as logic[16]
		this.ready = true
		return PacketDataResult.Success
	}

	/** Returns TCP's protocol identity, independent of readiness. */
	override fn GetProtocol() int
	{
		return IIpv4Payload.PROTOCOL_TCP as int
	}

	/** Prepared metadata does not acquire or police external provider ownership. */
	override fn IsReady() bool
	{
		return this.ready
	}

	/** Returns zero until successfully prepared. */
	override fn GetAvailableLength() int
	{
		if (!this.ready) {
			return 0
		}

		return (this.length as uint) as int
	}

	/** Header reads are cached; payload read errors propagate. Output is valid only on Success. */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

		if (!this.ready) {
			return PacketDataResult.NotReady
		}

		if (index < 0 || index >= ((this.length as uint) as int)) {
			return PacketDataResult.Invalid
		}

		if (index < 20) {
			value = this.TcpHeaderByte(index, sourcePortWord, destinationPortWord, sequenceHigh, sequenceLow, ackHigh, ackLow, windowWord, checksumWord, flagsByte)
			return PacketDataResult.Success
		}

		return this.payload.TryRead(index - 20, value)
	}

	/** Pure byte selection; no additional scheduled transaction. */
	inline fn TcpHeaderByte[](index: int, snapshotsourcePortWord: logic[16], snapshotdestinationPortWord: logic[16], snapshotsequenceHigh: logic[16], snapshotsequenceLow: logic[16], snapshotackHigh: logic[16], snapshotackLow: logic[16], snapshotwindowWord: logic[16], snapshotchecksumWord: logic[16], snapshotflagsByte: byte) byte
	{
		if (index == 0) { return (snapshotsourcePortWord >> 8) as byte }
		if (index == 1) { return snapshotsourcePortWord as byte }
		if (index == 2) { return (snapshotdestinationPortWord >> 8) as byte }
		if (index == 3) { return snapshotdestinationPortWord as byte }
		if (index == 4) { return (snapshotsequenceHigh >> 8) as byte }
		if (index == 5) { return snapshotsequenceHigh as byte }
		if (index == 6) { return (snapshotsequenceLow >> 8) as byte }
		if (index == 7) { return snapshotsequenceLow as byte }
		if (index == 8) { return (snapshotackHigh >> 8) as byte }
		if (index == 9) { return snapshotackHigh as byte }
		if (index == 10) { return (snapshotackLow >> 8) as byte }
		if (index == 11) { return snapshotackLow as byte }
		if (index == 12) { return 0x50 }
		if (index == 13) { return snapshotflagsByte }
		if (index == 14) { return (snapshotwindowWord >> 8) as byte }
		if (index == 15) { return snapshotwindowWord as byte }
		if (index == 16) { return (snapshotchecksumWord >> 8) as byte }
		if (index == 17) { return snapshotchecksumWord as byte }
		return 0x00
	}
}