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/services/IcmpEchoService.lvt

namespace Livt.Net

/**
 * Owns a checked ICMP echo response graph and keeps it immutable until explicit release.
 * Borrows request storage and an admission strategy. The successful response is
 * independent of RX; all request views close before TryHandle returns.
 */
component IcmpEchoService<S: IPacketData, A: IFrameAcceptance, CAPACITY: int = 128> : IResponseService
{
	// Borrowed source, policy and shared parsers; the response graph is owned.
	source: S
	acceptance: A
	ethernet: EthernetFrameParser<S>
	ipv4: Ipv4PacketParser<EthernetFrameParser<S>>
	response: IcmpEchoResponder<S, CAPACITY>

	// Endpoint configuration and publication state, owned by this service.
	localMac: byte[6]
	localIp: byte[4]
	ready: bool

	/**
	 * Borrows source, policy and shared parsers; creates an unpublished response.
	 * Parsers must bind this source in the same clock/reset domain. Serialize
	 * all parser users and close their descendants before handling a request.
	 */
	new(source: S, ethernet: EthernetFrameParser<S>, ipv4: Ipv4PacketParser<EthernetFrameParser<S>>, acceptance: A, localMac: byte[6], localIp: byte[4])
	{
		this.source = source
		this.acceptance = acceptance
		this.localMac = localMac
		this.localIp = localIp
		this.ethernet = ethernet
		this.ipv4 = ipv4
		this.response = new IcmpEchoResponder<S, CAPACITY>(ethernet, ipv4)
	}

	/**
	 * Prepares one supported local request; Busy preserves all previous bytes.
	 * Rejected/failed requests close their views and leave no published response.
	 */
	override fn TryHandle() ServiceResult
	{
		if (this.ready) {
			return ServiceResult.Busy
		}

		if (!this.source.IsReady()) {
			return ServiceResult.NotReady
		}

		this.ethernet.TryParse()

		if (!this.ethernet.IsReady()) {
			this.ethernet.Invalidate()
			return ServiceResult.Invalid
		}

		if (this.ethernet.GetEtherType() != IEthernetPayload.ETHER_TYPE_IPV4) {
			this.ethernet.Invalidate()
			return ServiceResult.Ignored
		}

		this.ipv4.TryParse()
		var protocol = this.ipv4.GetProtocol()
		var supported = this.ipv4.IsReady()

		if (!supported || protocol != IIpv4Payload.PROTOCOL_ICMP) {
			this.ipv4.Invalidate()
			this.ethernet.Invalidate()
			return ServiceResult.Ignored
		}

		var peer = this.ethernet.GetSourceMac()

		if (!this.acceptance.Accept(peer, IEthernetPayload.ETHER_TYPE_IPV4)) {
			this.ipv4.Invalidate()
			this.ethernet.Invalidate()
			return ServiceResult.Ignored
		}

		var result = this.response.TryPrepareParsed(this.localMac, this.localIp)

		if (result != PacketDataResult.Success) {
			this.response.Invalidate()
			return ServiceResult.Ignored
		}

		this.ready = true
		return ServiceResult.Prepared
	}

	/**
	 * Drops the response only after the caller has ended all response borrows.
	 */
	override fn TryReleaseResponse() PacketDataResult
	{
		if (!this.ready) {
			return PacketDataResult.Invalid
		}

		this.ready = false
		this.response.Invalidate()
		return PacketDataResult.Success
	}

	/**
	 * Reports this service's publication, not mere internal preparation progress.
	 */
	override fn IsReady() bool
	{
		return this.ready
	}

	/**
	 * Returns complete padded frame bytes, or zero before publication.
	 */
	override fn GetAvailableLength() int
	{
		if (!this.ready) {
			return 0
		}

		return this.response.GetAvailableLength()
	}

	/**
	 * Reads a published response byte; output is data only on Success.
	 */
	override fn TryRead(index: int, value: out byte) PacketDataResult
	{
		value = 0x00

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

		return this.response.TryRead(index, value)
	}
}