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/IcmpEchoResponder.lvt
namespace Livt.Net
/**
* Checked request recognition and compact prepared reply composition. One
* serialized owner must finish all reads/TX borrows before preparing or
* invalidating again. Preparation snapshots request metadata and echo data;
* emission does not read the request.
*/
component IcmpEchoResponder<
S: IPacketData,
FRAME_CAPACITY: int = 128,
B: IWritablePacketData<PacketDataGeometry<FRAME_CAPACITY>> & new() = RamPacketData<FRAME_CAPACITY>
> : IPacketData
{
static assert FRAME_CAPACITY >= 42 && FRAME_CAPACITY <= 65535, "Invalid ICMP frame capacity"
ethernet: EthernetFrameParser<S>
ipv4: Ipv4PacketParser<EthernetFrameParser<S>>
data: B
source0: logic[16]
source1: logic[16]
source2: logic[16]
destination0: logic[16]
destination1: logic[16]
destination2: logic[16]
ipv4Length: logic[16]
identificationWord: logic[16]
sourceIpHigh: logic[16]
sourceIpLow: logic[16]
destinationIpHigh: logic[16]
destinationIpLow: logic[16]
icmpChecksum: logic[16]
icmpIdentifier: logic[16]
icmpSequence: logic[16]
dataLength: logic[16]
frameLength: logic[16]
ipv4Checksum: logic[16]
ready: bool
new(ethernet: EthernetFrameParser<S>, ipv4: Ipv4PacketParser<EthernetFrameParser<S>>)
{
this.ethernet = ethernet
this.ipv4 = ipv4
data = new B()
}
public fn Invalidate()
{
ipv4.Invalidate()
ethernet.Invalidate()
this.ClearResponse()
}
fn ClearResponse()
{
ready = false
frameLength = 0
dataLength = 0
if (data.IsReady()) {
data.TryRelease()
}
else {
data.TryClear()
}
}
public fn TryPrepare(localMac: byte[6], localIp: byte[4]) PacketDataResult
{
this.Invalidate()
ethernet.TryParse()
if (!ethernet.IsReady() || ethernet.GetEtherType() != IEthernetPayload.ETHER_TYPE_IPV4) {
return PacketDataResult.Invalid
}
if (ipv4.TryParse() != PacketParseResult.Success) {
return PacketDataResult.Invalid
}
return this.TryPrepareParsed(localMac, localIp)
}
/** Accumulates the ICMP checksum while copying into the response snapshot. */
public fn TryPrepareParsed(localMac: byte[6], localIp: byte[4]) PacketDataResult
{
this.ClearResponse()
if (!ethernet.IsReady() || ethernet.GetEtherType() != IEthernetPayload.ETHER_TYPE_IPV4
|| ipv4.GetStatus() != PacketParseResult.Success) {
return PacketDataResult.Invalid
}
if (ipv4.GetProtocol() != IIpv4Payload.PROTOCOL_ICMP || ipv4.GetAvailableLength() < 8) {
return PacketDataResult.Invalid
}
var destination = ipv4.GetDestinationIpv4()
if (destination[0] != localIp[0] || destination[1] != localIp[1] || destination[2] != localIp[2] || destination[3] != localIp[3]) {
return PacketDataResult.Invalid
}
var echo: logic[64] = 0
for (var i = 0; i < 8; i++) {
var value: byte = 0x00
if (ipv4.TryRead(i, value) != PacketDataResult.Success) {
return PacketDataResult.Invalid
}
echo = (echo << 8) | ((value as uint) as logic[64])
}
if (((echo >> 48) as logic[16]) != 0x0800) {
return PacketDataResult.Invalid
}
var peerMac = ethernet.GetSourceMac()
var peerIp = ipv4.GetSourceIpv4()
var id = ipv4.GetIdentification()
var identification: byte[2] = [NetworkOrder.HighByte(id), NetworkOrder.LowByte(id)]
var identifier: byte[2] = [(echo >> 24) as byte, (echo >> 16) as byte]
var sequence: byte[2] = [(echo >> 8) as byte, echo as byte]
var length = ipv4.GetAvailableLength() - 8
if (length < 0 || length > FRAME_CAPACITY - 28) {
this.Invalidate()
return PacketDataResult.Invalid
}
var sum = NetworkOrder.Word(identifier[0], identifier[1]) + NetworkOrder.Word(sequence[0], sequence[1])
var pending: byte = 0x00
var highByte = true
for (var i = 0; i < length; i++) {
var value: byte = 0x00
if (ipv4.TryRead(8 + i, value) != PacketDataResult.Success || data.TryWrite(i, value) != PacketDataResult.Success) {
this.Invalidate()
return PacketDataResult.Invalid
}
if (highByte) {
pending = value
highByte = false
}
else {
sum = sum + NetworkOrder.Word(pending, value)
highByte = true
}
}
if (!highByte) {
sum = sum + NetworkOrder.Word(pending, 0x00)
}
ipv4.Invalidate()
ethernet.Invalidate()
var published = data.TryPublish()
if (published != PacketDataResult.Success) {
this.Invalidate()
return published
}
source0 = NetworkOrder.Word(localMac[0], localMac[1]) as logic[16]
source1 = NetworkOrder.Word(localMac[2], localMac[3]) as logic[16]
source2 = NetworkOrder.Word(localMac[4], localMac[5]) as logic[16]
destination0 = NetworkOrder.Word(peerMac[0], peerMac[1]) as logic[16]
destination1 = NetworkOrder.Word(peerMac[2], peerMac[3]) as logic[16]
destination2 = NetworkOrder.Word(peerMac[4], peerMac[5]) as logic[16]
ipv4Length = (28 + length) as logic[16]
identificationWord = NetworkOrder.Word(identification[0], identification[1]) as logic[16]
sourceIpHigh = NetworkOrder.Word(localIp[0], localIp[1]) as logic[16]
sourceIpLow = NetworkOrder.Word(localIp[2], localIp[3]) as logic[16]
destinationIpHigh = NetworkOrder.Word(peerIp[0], peerIp[1]) as logic[16]
destinationIpLow = NetworkOrder.Word(peerIp[2], peerIp[3]) as logic[16]
// Snapshot both partial sums before yielding; ready remains false until publication.
var headerLeft = 0x4500 + (28 + length)
+ NetworkOrder.Word(identification[0], identification[1]) + 0x4000
var headerRight = 0x4000 + NetworkOrder.ByteValue(IIpv4Payload.PROTOCOL_ICMP)
+ NetworkOrder.Word(localIp[0], localIp[1]) + NetworkOrder.Word(localIp[2], localIp[3])
+ NetworkOrder.Word(peerIp[0], peerIp[1]) + NetworkOrder.Word(peerIp[2], peerIp[3])
state {
}
var headerSum = headerLeft + headerRight
state {
}
var foldedHeader = ChecksumArithmetic.Fold(headerSum)
state {
}
ipv4Checksum = (0xFFFF - ChecksumArithmetic.Fold(foldedHeader)) as logic[16]
icmpChecksum = ChecksumArithmetic.Finish(sum) as logic[16]
icmpIdentifier = NetworkOrder.Word(identifier[0], identifier[1]) as logic[16]
icmpSequence = NetworkOrder.Word(sequence[0], sequence[1]) as logic[16]
dataLength = length as logic[16]
var completeLength = 42 + length
if (completeLength < 60) { completeLength = 60 }
frameLength = completeLength as logic[16]
ready = true
return PacketDataResult.Success
}
override fn IsReady() bool
{
return ready && data.IsReady()
}
override fn GetAvailableLength() int
{
if (!this.IsReady()) { return 0 }
return (frameLength as uint) as int
}
/** Reads fixed headers directly and delegates only echo-data bytes to the selected backend. */
override fn TryRead(index: int, value: out byte) PacketDataResult
{
value = 0x00
if (!this.IsReady()) { return PacketDataResult.NotReady }
if (index < 0 || index >= ((frameLength as uint) as int)) { return PacketDataResult.Invalid }
if (index < 42) {
value = this.HeaderByte(index, source0, source1, source2, destination0, destination1, destination2,
ipv4Length, identificationWord, sourceIpHigh, sourceIpLow, destinationIpHigh, destinationIpLow,
ipv4Checksum, icmpChecksum, icmpIdentifier, icmpSequence)
return PacketDataResult.Success
}
if (index < 42 + ((dataLength as uint) as int)) {
return data.TryRead(index - 42, value)
}
return PacketDataResult.Success
}
/** Pure selector over compact response metadata; constants remain unstored. */
inline fn HeaderByte[](index: int, snapshotsource0: logic[16], snapshotsource1: logic[16], snapshotsource2: logic[16],
snapshotdestination0: logic[16], snapshotdestination1: logic[16], snapshotdestination2: logic[16],
snapshotipv4Length: logic[16], snapshotidentificationWord: logic[16], snapshotsourceIpHigh: logic[16],
snapshotsourceIpLow: logic[16], snapshotdestinationIpHigh: logic[16], snapshotdestinationIpLow: logic[16],
snapshotipv4Checksum: logic[16], snapshoticmpChecksum: logic[16], snapshoticmpIdentifier: logic[16],
snapshoticmpSequence: logic[16]) byte
{
if (index == 0) { return (snapshotdestination0 >> 8) as byte }
if (index == 1) { return snapshotdestination0 as byte }
if (index == 2) { return (snapshotdestination1 >> 8) as byte }
if (index == 3) { return snapshotdestination1 as byte }
if (index == 4) { return (snapshotdestination2 >> 8) as byte }
if (index == 5) { return snapshotdestination2 as byte }
if (index == 6) { return (snapshotsource0 >> 8) as byte }
if (index == 7) { return snapshotsource0 as byte }
if (index == 8) { return (snapshotsource1 >> 8) as byte }
if (index == 9) { return snapshotsource1 as byte }
if (index == 10) { return (snapshotsource2 >> 8) as byte }
if (index == 11) { return snapshotsource2 as byte }
if (index == 12) { return 0x08 }
if (index == 14) { return 0x45 }
if (index == 16) { return (snapshotipv4Length >> 8) as byte }
if (index == 17) { return snapshotipv4Length as byte }
if (index == 18) { return (snapshotidentificationWord >> 8) as byte }
if (index == 19) { return snapshotidentificationWord as byte }
if (index == 20 || index == 22) { return 0x40 }
if (index == 23) { return 0x01 }
if (index == 24) { return (snapshotipv4Checksum >> 8) as byte }
if (index == 25) { return snapshotipv4Checksum as byte }
if (index == 26) { return (snapshotsourceIpHigh >> 8) as byte }
if (index == 27) { return snapshotsourceIpHigh as byte }
if (index == 28) { return (snapshotsourceIpLow >> 8) as byte }
if (index == 29) { return snapshotsourceIpLow as byte }
if (index == 30) { return (snapshotdestinationIpHigh >> 8) as byte }
if (index == 31) { return snapshotdestinationIpHigh as byte }
if (index == 32) { return (snapshotdestinationIpLow >> 8) as byte }
if (index == 33) { return snapshotdestinationIpLow as byte }
if (index == 36) { return (snapshoticmpChecksum >> 8) as byte }
if (index == 37) { return snapshoticmpChecksum as byte }
if (index == 38) { return (snapshoticmpIdentifier >> 8) as byte }
if (index == 39) { return snapshoticmpIdentifier as byte }
if (index == 40) { return (snapshoticmpSequence >> 8) as byte }
if (index == 41) { return snapshoticmpSequence as byte }
return 0x00
}
}