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/ipv4/Ipv4Packet.lvt

namespace Livt.Net

/**
 * Fixed 20-byte IPv4 header, DF, TTL=64, with a typed protocol payload.
 * Prepare the child first and keep it unchanged until emission finishes.
 * Only variable header fields are stored; payload reads delegate to the child.
 */
component Ipv4Packet<P: IPacketData & IIpv4Payload> : IPacketData, IEthernetPayload
{
	// Borrowed prepared payload; must remain stable until emission finishes.
	payload: P

	// Owned network-order words; constants are selected during emission.
	length: logic[16]
	identificationWord: logic[16]
	protocolByte: byte
	checksumWord: logic[16]
	sourceHigh: logic[16]
	sourceLow: logic[16]
	destinationHigh: logic[16]
	destinationLow: logic[16]
	ready: bool

	/**
	 * Borrows the fixed payload; starts unprepared and performs no payload reads.
	 */
	new(payload: P)
	{
		this.payload = payload
	}

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

	/**
	 * All address and identification arrays use network byte order.
	 */
	public fn TryPrepare(sourceIp: byte[4], destinationIp: byte[4], identification: byte[2]) PacketDataResult
	{
		this.Invalidate()

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

		var size = payload.GetAvailableLength()
		var protocol = payload.GetProtocol()

		if (size <= 0 || size > 65515 || protocol < 0 || protocol > 255) {
			return PacketDataResult.Invalid
		}

		var totalLength = size + 20
		// Validate in the public integer domain before narrowing stored fields.
		length = totalLength as logic[16]
		identificationWord = NetworkOrder.Word(identification[0], identification[1]) as logic[16]
		protocolByte = protocol as byte
		sourceHigh = NetworkOrder.Word(sourceIp[0], sourceIp[1]) as logic[16]
		sourceLow = NetworkOrder.Word(sourceIp[2], sourceIp[3]) as logic[16]
		destinationHigh = NetworkOrder.Word(destinationIp[0], destinationIp[1]) as logic[16]
		destinationLow = NetworkOrder.Word(destinationIp[2], destinationIp[3]) as logic[16]

		// Sum the fixed header words directly, without constructing/rereading bytes.
		checksumWord = Ipv4HeaderChecksum.Calculate(totalLength, identification,
			sourceIp, destinationIp, protocol as byte) as logic[16]
		ready = true
		return PacketDataResult.Success
	}

	/**
	 * Returns the Ethernet II EtherType identifying this payload, independent of readiness.
	 */
	override fn GetEtherType() int
	{
		return IEthernetPayload.ETHER_TYPE_IPV4
	}

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

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

		return (length 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..19 are the IPv4 header; remaining bytes delegate to the payload.
	 */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

		if (!ready) {
			return PacketDataResult.NotReady
		}

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

		if (index < 20) {
			value = this.Ipv4HeaderByte(index, length, identificationWord, protocolByte, checksumWord, sourceHigh, sourceLow, destinationHigh, destinationLow)
			return PacketDataResult.Success
		}

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

	/** Pure selector over the prepared snapshot. */
	inline fn Ipv4HeaderByte[](index: int, snapshotlength: logic[16], snapshotidentificationWord: logic[16], snapshotprotocolByte: byte, snapshotchecksumWord: logic[16], snapshotsourceHigh: logic[16], snapshotsourceLow: logic[16], snapshotdestinationHigh: logic[16], snapshotdestinationLow: logic[16]) byte
	{
		if (index == 0) { return 0x45 }
		elif (index == 2) { return (snapshotlength >> 8) as byte }
		elif (index == 3) { return snapshotlength as byte }
		elif (index == 4) { return (snapshotidentificationWord >> 8) as byte }
		elif (index == 5) { return snapshotidentificationWord as byte }
		elif (index == 6 || index == 8) { return 0x40 }
		elif (index == 9) { return snapshotprotocolByte }
		elif (index == 10) { return (snapshotchecksumWord >> 8) as byte }
		elif (index == 11) { return snapshotchecksumWord as byte }
		elif (index == 12) { return (snapshotsourceHigh >> 8) as byte }
		elif (index == 13) { return snapshotsourceHigh as byte }
		elif (index == 14) { return (snapshotsourceLow >> 8) as byte }
		elif (index == 15) { return snapshotsourceLow as byte }
		elif (index == 16) { return (snapshotdestinationHigh >> 8) as byte }
		elif (index == 17) { return snapshotdestinationHigh as byte }
		elif (index == 18) { return (snapshotdestinationLow >> 8) as byte }
		elif (index == 19) { return snapshotdestinationLow as byte }
		return 0x00
	}
}