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/udp/UdpEthernetPacket.lvt

namespace Livt.Net

/**
 * Reusable prepared Ethernet/IPv4/UDP view over one borrowed payload publication.
 * No frame buffer. Caller resolves/configures destination MAC before construction;
 * all four address arrays must remain stable. One owner serializes Prepare/Release
 * and retains the payload through MAC completion. IPv4 DF, TTL64. Prepare omits
 * the UDP checksum; PrepareChecksummed includes it. No IPv6, fragmentation, ARP
 * or retransmission here.
 */
component UdpEthernetPacket<P: IPacketData> : IPacketData
{
	// Borrowed stable payload and endpoint addresses; no payload ownership transfers.
	payload: P
	localMac: byte[6]
	remoteMac: byte[6]
	localIp: byte[4]
	remoteIp: byte[4]

	// Published scalar header snapshot, held until the transmitter releases it.
	published: bool
	payloadLength: int
	frameLength: int
	udpChecksum: int
	payloadSum: InternetChecksum
	checksum: int
	sourcePort: int
	destinationPort: int
	identity: int

	/**
	 * Binds endpoint identity in the same context; the outer network owner resolves MAC.
	 */
	new(payload: P, localMac: byte[6], localIp: byte[4], remoteMac: byte[6], remoteIp: byte[4])
	{
		this.payloadSum = new InternetChecksum()
		this.payload = payload
		this.localMac = localMac
		this.localIp = localIp
		this.remoteMac = remoteMac
		this.remoteIp = remoteIp
	}

	/**
	 * Captures one valid payload of up to 1472 bytes; rejects overlapping publication.
	 */
	public fn Prepare(sourcePort: int, destinationPort: int, identification: int) bool
	{
		return this.PrepareInternal(sourcePort, destinationPort, identification, false)
	}

	/** Scans the stable payload and publishes only after its UDP checksum is ready. */
	public fn PrepareChecksummed(sourcePort: int, destinationPort: int, identification: int) bool
	{
		return this.PrepareInternal(sourcePort, destinationPort, identification, true)
	}

	fn PrepareInternal(sourcePort: int, destinationPort: int, identification: int, checksummed: bool) bool
	{
		if (this.published || sourcePort < 0 || sourcePort > 65535
			|| destinationPort < 1 || destinationPort > 65535
			|| identification < 0 || identification > 65535 || !this.payload.IsReady()) {
			return false
		}

		var length = this.payload.GetAvailableLength()

		if (length < 0 || length > 1472) {
			return false
		}

		this.payloadLength = length
		this.frameLength = length + 42

		if (this.frameLength < 60) {
			this.frameLength = 60
		}

		this.sourcePort = sourcePort
		this.destinationPort = destinationPort
		this.identity = identification
		var sum = 0x4500 + (length + 28) + identification + 0x4000 + 0x4011
			+ NetworkOrder.Word(this.localIp[0], this.localIp[1])
			+ NetworkOrder.Word(this.localIp[2], this.localIp[3])
			+ NetworkOrder.Word(this.remoteIp[0], this.remoteIp[1])
			+ NetworkOrder.Word(this.remoteIp[2], this.remoteIp[3])
		this.checksum = ChecksumArithmetic.Finish(sum)
		this.udpChecksum = 0
		if (checksummed) {
			this.payloadSum.Reset()
			for (var index = 0; index < length; index++) {
				var value: byte = 0
				if (this.payload.TryRead(index, value) != PacketDataResult.Success) { return false }
				this.payloadSum.AddByte(value)
			}
			this.udpChecksum = UdpChecksum.Calculate(this.localIp, this.remoteIp,
				sourcePort, destinationPort, length + 8, this.payloadSum.GetWordSum())
		}
		this.published = true
		return true
	}

	/**
	 * Ends the header view after every dependent read and MAC completion has finished.
	 */
	public fn Release()
	{
		this.published = false
	}

	/**
	 * Reports the retained publication without acquiring it.
	 */
	override fn IsReady() bool
	{
		return this.published
	}

	/**
	 * Ethernet size excludes FCS/preamble, includes zero padding to minimum size.
	 */
	override fn GetAvailableLength() int
	{
		if (this.published) {
			return this.frameLength
		}

		return 0
	}

	/**
	 * Header reads use network order; payload failures propagate and padding is zero.
	 */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0

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

		if (index < 0 || index >= this.frameLength) {
			return PacketDataResult.Invalid
		}

		if (index < 42) {
			value = UdpHeader.GetByte(index, this.localMac, this.localIp, this.remoteMac,
				this.remoteIp, this.payloadLength, this.identity as uint, this.checksum,
				this.sourcePort, this.destinationPort, this.udpChecksum)
		} elif (index < this.payloadLength + 42) {
			return this.payload.TryRead(index - 42, value)
		}
		return PacketDataResult.Success
	}
}