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/icmp/IcmpEchoReply.lvt

namespace Livt.Net

/**
 * Prepared echo reply borrowing a published payload provider. Only header
 * metadata is stored here. Invalidate ancestors before replacing the payload;
 * keep the provider stable until every prepared response/TX borrow has ended.
 */
component IcmpEchoReply<P: IPacketData> : IPacketData, IIpv4Payload
{
	// Borrowed echo data; must remain stable until emission finishes.
	payload: P

	// Prepared echo header metadata and bounded data length.
	identifier: logic[16]
	sequence: logic[16]
	checksum: logic[16]
	dataLength: logic[16]
	ready: bool

	// Owned accumulator used only while preparing the checksum.
	calculator: InternetChecksum

	/**
	 * Borrows the fixed payload and owns a checksum calculator; starts unprepared.
	 */
	new(payload: P)
	{
		this.payload = payload;
		calculator = new InternetChecksum()
	}

	/**
	 * Discards prepared metadata without changing the payload. Finish all dependent
	 * reads and transmit borrows before invalidating.
	 */
	public fn Invalidate()
	{
		ready = false;
		dataLength = 0
	}

	/**
	 * Failure invalidates the previous reply. Compute checksum once, before reads.
	 */
	public fn TryPrepare(identifier: byte[2], sequence: byte[2]) PacketDataResult
	{
		this.Invalidate()

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

		var size = payload.GetAvailableLength()

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

		this.identifier = NetworkOrder.Word(identifier[0], identifier[1]) as logic[16]
		this.sequence = NetworkOrder.Word(sequence[0], sequence[1]) as logic[16]
		calculator.Reset()

		// Reply type, code and checksum are all zero during calculation.
		calculator.AddByte(identifier[0])
		calculator.AddByte(identifier[1])
		calculator.AddByte(sequence[0])
		calculator.AddByte(sequence[1])

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

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

			calculator.AddByte(value)
		}

		checksum = calculator.GetChecksum() as logic[16]
		dataLength = size as logic[16]
		ready = true
		return PacketDataResult.Success
	}

	/**
	 * Reports prepared metadata; the owner must still keep all borrowed payloads stable.
	 */
	override fn IsReady() bool
	{
		return ready
	}

	/**
	 * Returns the IPv4 protocol number identifying this payload, independent of readiness.
	 */
	override fn GetProtocol() int
	{
		return IIpv4Payload.PROTOCOL_ICMP as int
	}

	/**
	 * Returns the prepared packet length in bytes, or zero while unprepared.
	 */
	override fn GetAvailableLength() int
	{
		if (!ready) {
			return 0
		}

		return 8 + ((dataLength as uint) as int)
	}

	/**
	 * Reads a packet-relative byte; returns NotReady while unprepared and Invalid
	 * outside the prepared length. Delegated payload errors propagate; the output
	 * is data only on Success. Keep the prepared graph stable throughout the call.
	 * Bytes 0..1: reply type/code; 2..3: checksum; 4..5: identifier;
	 * 6..7: sequence; bytes 8 onward delegate to the echo payload.
	 */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

		if (!ready) {
			return PacketDataResult.NotReady
		}

		if (index < 0 || index >= 8 + ((dataLength as uint) as int)) {
			return PacketDataResult.Invalid
		}

		if (index < 8) {
			value = this.IcmpHeaderByte(index, identifier, sequence, checksum)
			return PacketDataResult.Success
		}

		return payload.TryRead(index - 8, value)
	}

	/** Pure byte selection; no additional scheduled transaction. */
	inline fn IcmpHeaderByte[](index: int, snapshotidentifier: logic[16], snapshotsequence: logic[16], snapshotchecksum: logic[16]) byte
	{
		if (index == 2) { return (snapshotchecksum >> 8) as byte }
		if (index == 3) { return snapshotchecksum as byte }
		if (index == 4) { return (snapshotidentifier >> 8) as byte }
		if (index == 5) { return snapshotidentifier as byte }
		if (index == 6) { return (snapshotsequence >> 8) as byte }
		if (index == 7) { return snapshotsequence as byte }
		return 0x00
	}
}