namespace XFESystem.Network;
/// <summary>
/// One-owner, bounded IPv4 client stack over real Ethernet. Static configuration,
/// ARP, UDP DNS and one active TCP stream; no host sockets or implicit fallback.
/// </summary>
public sealed class Ipv4Network
{
readonly IEthernetDevice device;
readonly INetworkPlatform platform;
readonly Ipv4Configuration configuration;
readonly byte[] receive = new byte[2048], transmit = new byte[1514], payload = new byte[1500];
readonly uint[] neighborAddresses = new uint[8];
readonly byte[][] neighborMacs = Enumerable.Range(0, 8).Select(_ => new byte[6]).ToArray();
int neighborCursor; uint awaitingNeighbor;
ushort ipId, dnsId, dnsPort;
string? dnsHost, dnsError;
uint dnsAddress;
TcpConnection? active;
public Ipv4Network(IEthernetDevice device, INetworkPlatform platform, Ipv4Configuration configuration)
{
this.device = device; this.platform = platform; this.configuration = configuration;
if (!NetworkPackets.IsUnicastMac(device.MacAddress) ||
configuration.Address == 0 || configuration.Gateway == 0 || configuration.DnsServer == 0)
throw new ArgumentException("The network requires a real unicast MAC and explicit IPv4/NAT configuration.");
}
internal long Now => platform.Milliseconds;
internal void Wait() => platform.Wait(1);
internal void Trace(string value) => platform.Trace(value);
internal static void CheckTimeout(long now, long deadline, string operation)
{ if (now >= deadline) throw new IOException(operation + " timed out."); }
internal long Deadline(int milliseconds)
{
if (milliseconds is < 1 or > 120_000) throw new ArgumentOutOfRangeException(nameof(milliseconds));
return checked(Now + milliseconds);
}
public Stream OpenTcp(string host, int port, int timeoutMilliseconds = 15_000)
=> OpenTcpUntil(host, port, Deadline(timeoutMilliseconds), timeoutMilliseconds);
public Stream OpenTcpUntil(string host, int port, long deadlineMilliseconds, int streamTimeoutMilliseconds = 15_000)
{
if (active != null) throw new InvalidOperationException("The one-owner network already has an active TCP stream.");
if (port is < 1 or > 65535) throw new ArgumentOutOfRangeException(nameof(port));
_ = Deadline(streamTimeoutMilliseconds);
long deadline = deadlineMilliseconds;
CheckTimeout(Now, deadline, "TCP open");
if (deadline - Now > 120000) throw new ArgumentOutOfRangeException(nameof(deadlineMilliseconds));
uint address = NetworkPackets.TryParseAddress(host, out uint literal) ? literal : Resolve(host, deadline);
if (address == 0 || address == configuration.Address || (address >> 28) >= 14)
throw new IOException("TCP destination is not a usable unicast IPv4 address.");
EnsureNeighbor(address, deadline);
ushort localPort = (ushort)(49152 + (platform.Random32() & 16383));
CheckTimeout(Now, deadline, "TCP open");
var connection = new TcpConnection(this, address, localPort, (ushort)port, platform.Random32(), streamTimeoutMilliseconds);
active = connection;
try { connection.Connect(deadline); return connection; }
catch { active = null; connection.Abort(deadline); throw; }
}
public uint Resolve(string host, int timeoutMilliseconds = 5000) => Resolve(host, Deadline(timeoutMilliseconds));
uint Resolve(string host, long deadline)
{
if (active != null) throw new InvalidOperationException("DNS cannot interrupt another active TCP owner.");
dnsHost = DnsMessage.NormalizeName(host); dnsId = (ushort)platform.Random32();
dnsPort = (ushort)(49152 + (platform.Random32() & 16383)); dnsAddress = 0; dnsError = null;
byte[] query = new byte[512]; int length = DnsMessage.WriteQuery(query, dnsId, dnsHost);
try
{
long nextSend = 0; int attempts = 0;
while (dnsAddress == 0)
{
CheckTimeout(Now, deadline, "DNS");
if (Now >= nextSend)
{
if (attempts++ >= 4) throw new IOException("DNS did not answer after bounded retransmission.");
SendUdp(configuration.DnsServer, dnsPort, 53, query.AsSpan(0, length), deadline);
nextSend = Now + Math.Min(1000, 250 << (attempts - 1));
}
Poll(deadline);
if (dnsError != null) throw new IOException(dnsError);
if (dnsAddress == 0) Wait();
}
Trace("XFE_NETWORK_DNS|Host|" + dnsHost + "|Address|" + NetworkPackets.Format(dnsAddress) + "\n");
return dnsAddress;
}
finally { dnsHost = null; }
}
uint NextHop(uint remote) => (remote & configuration.Mask) == (configuration.Address & configuration.Mask) ? remote : configuration.Gateway;
byte[]? FindNeighbor(uint address)
{
for (int i = 0; i < neighborAddresses.Length; i++) if (neighborAddresses[i] == address) return neighborMacs[i];
return null;
}
byte[] EnsureNeighbor(uint remote, long deadline)
{
uint address = NextHop(remote); byte[]? existing = FindNeighbor(address);
if (existing != null) return existing;
awaitingNeighbor = address;
try
{
long nextSend = 0; int attempts = 0;
while ((existing = FindNeighbor(address)) == null)
{
CheckTimeout(Now, deadline, "ARP");
if (Now >= nextSend)
{
if (attempts++ >= 4) throw new IOException("ARP next hop did not respond.");
SendArp(address, default, false, deadline); nextSend = Now + 250;
}
Poll(deadline); if (FindNeighbor(address) == null) Wait();
}
return existing;
}
finally { awaitingNeighbor = 0; }
}
void SendFrame(int length, long deadline)
{
CheckTimeout(Now, deadline, "Ethernet transmit");
int padded = Math.Max(length, 60);
transmit.AsSpan(length, padded - length).Clear();
if (!device.Send(transmit.AsSpan(0, padded), deadline)) throw new IOException("Ethernet transmit failed or timed out.");
CheckTimeout(Now, deadline, "Ethernet transmit");
}
void Ethernet(ReadOnlySpan<byte> destination, ushort protocol)
{
destination.CopyTo(transmit); device.MacAddress.CopyTo(transmit.AsSpan(6)); NetworkPackets.Write16(transmit, 12, protocol);
}
void SendArp(uint target, ReadOnlySpan<byte> targetMac, bool response, long deadline)
{
Span<byte> broadcast = stackalloc byte[6]; broadcast.Fill(255);
Ethernet(response ? targetMac : broadcast, 0x0806);
Span<byte> arp = transmit.AsSpan(14, 28); arp.Clear();
NetworkPackets.Write16(arp, 0, 1); NetworkPackets.Write16(arp, 2, 0x0800); arp[4] = 6; arp[5] = 4;
NetworkPackets.Write16(arp, 6, response ? (ushort)2 : (ushort)1);
device.MacAddress.CopyTo(arp[8..]); NetworkPackets.Write32(arp, 14, configuration.Address);
if (response) targetMac.CopyTo(arp[18..]); NetworkPackets.Write32(arp, 24, target);
SendFrame(42, deadline);
}
void ReceiveArp(ReadOnlySpan<byte> frame, long deadline)
{
if (frame.Length < 42) return;
ReadOnlySpan<byte> arp = frame[14..];
if (NetworkPackets.Read16(arp, 0) != 1 || NetworkPackets.Read16(arp, 2) != 0x0800 || arp[4] != 6 || arp[5] != 4 ||
!arp.Slice(8, 6).SequenceEqual(frame.Slice(6, 6)) || !NetworkPackets.IsUnicastMac(arp.Slice(8, 6)) ||
NetworkPackets.Read32(arp, 24) != configuration.Address) return;
uint sender = NetworkPackets.Read32(arp, 14); ushort operation = NetworkPackets.Read16(arp, 6);
if (operation == 2 && sender == awaitingNeighbor && arp.Slice(18, 6).SequenceEqual(device.MacAddress))
{
int slot = neighborCursor; neighborCursor = (neighborCursor + 1) % neighborAddresses.Length; neighborAddresses[slot] = sender;
arp.Slice(8, 6).CopyTo(neighborMacs[slot]);
Trace("XFE_NETWORK_ARP|NextHop|" + NetworkPackets.Format(sender) + "|Resolved|1\n");
}
if (operation == 1 && sender != 0) SendArp(sender, arp.Slice(8, 6), true, deadline);
}
void SendIpv4(uint destination, byte protocol, int length, ReadOnlySpan<byte> nextHopMac, long deadline)
{
if (length < 0 || length > 1480) throw new IOException("IPv4 payload exceeds the fixed Ethernet MTU.");
Ethernet(nextHopMac, 0x0800); Span<byte> ip = transmit.AsSpan(14, 20); ip.Clear();
ip[0] = 0x45; NetworkPackets.Write16(ip, 2, (ushort)(20 + length)); NetworkPackets.Write16(ip, 4, ++ipId);
NetworkPackets.Write16(ip, 6, 0x4000); ip[8] = 64; ip[9] = protocol;
NetworkPackets.Write32(ip, 12, configuration.Address); NetworkPackets.Write32(ip, 16, destination);
NetworkPackets.Write16(ip, 10, NetworkPackets.Checksum(ip)); payload.AsSpan(0, length).CopyTo(transmit.AsSpan(34));
SendFrame(34 + length, deadline);
}
void SendUdp(uint destination, ushort sourcePort, ushort destinationPort, ReadOnlySpan<byte> data, long deadline)
{
if (data.Length > 1472) throw new IOException("UDP datagram exceeds MTU.");
byte[] mac = EnsureNeighbor(destination, deadline); Span<byte> segment = payload.AsSpan(0, data.Length + 8); segment.Clear();
NetworkPackets.Write16(segment, 0, sourcePort); NetworkPackets.Write16(segment, 2, destinationPort);
NetworkPackets.Write16(segment, 4, (ushort)segment.Length); data.CopyTo(segment[8..]);
ushort checksum = NetworkPackets.TransportChecksum(configuration.Address, destination, 17, segment);
NetworkPackets.Write16(segment, 6, checksum == 0 ? (ushort)65535 : checksum);
SendIpv4(destination, 17, segment.Length, mac, deadline);
}
internal void SendTcp(uint destination, ushort sourcePort, ushort destinationPort, uint sequence, uint acknowledgement,
byte flags, ushort window, ReadOnlySpan<byte> data, long deadline, bool syn = false)
{
byte[] mac = FindNeighbor(NextHop(destination)) ?? throw new IOException("TCP next-hop ARP entry is unavailable.");
int header = syn ? 24 : 20;
if (data.Length > 1460 || header + data.Length > 1480) throw new IOException("TCP segment exceeds MTU.");
Span<byte> segment = payload.AsSpan(0, header + data.Length); segment.Clear();
NetworkPackets.Write16(segment, 0, sourcePort); NetworkPackets.Write16(segment, 2, destinationPort);
NetworkPackets.Write32(segment, 4, sequence); NetworkPackets.Write32(segment, 8, acknowledgement);
segment[12] = (byte)((header / 4) << 4); segment[13] = flags; NetworkPackets.Write16(segment, 14, window);
if (syn) { segment[20] = 2; segment[21] = 4; NetworkPackets.Write16(segment, 22, 1460); }
data.CopyTo(segment[header..]); NetworkPackets.Write16(segment, 16, NetworkPackets.TransportChecksum(configuration.Address, destination, 6, segment));
SendIpv4(destination, 6, segment.Length, mac, deadline);
}
internal void Poll(long deadline)
{
// A finite drain gives the scheduler/UI another chance even under flood.
for (int budget = 0; budget < 64; budget++)
{
CheckTimeout(Now, deadline, "Ethernet poll");
int length = device.Receive(receive);
CheckTimeout(Now, deadline, "Ethernet poll");
if (length == 0) return;
if (length < 14 || length > receive.Length) continue;
ReadOnlySpan<byte> frame = receive.AsSpan(0, length);
ushort type = NetworkPackets.Read16(frame, 12);
if (type == 0x0806) { ReceiveArp(frame, deadline); continue; }
if (type != 0x0800 || length < 34) continue;
ReadOnlySpan<byte> ip = frame[14..]; int header = (ip[0] & 15) * 4, total = NetworkPackets.Read16(ip, 2);
if ((ip[0] >> 4) != 4 || header < 20 || header > ip.Length || total < header || total > ip.Length ||
NetworkPackets.Checksum(ip[..header]) != 0 || (NetworkPackets.Read16(ip, 6) & 0x3fff) != 0 || ip[8] == 0 ||
NetworkPackets.Read32(ip, 16) != configuration.Address) continue;
uint source = NetworkPackets.Read32(ip, 12); byte protocol = ip[9]; ReadOnlySpan<byte> body = ip.Slice(header, total - header);
if (protocol == 6 && body.Length >= 20 && NetworkPackets.TransportChecksum(source, configuration.Address, 6, body) == 0)
active?.Receive(source, body, deadline);
if (protocol != 17 || dnsHost == null || source != configuration.DnsServer || body.Length < 8 ||
NetworkPackets.Read16(body, 0) != 53 || NetworkPackets.Read16(body, 2) != dnsPort) continue;
int udpLength = NetworkPackets.Read16(body, 4);
if (udpLength < 8 || udpLength > body.Length) continue;
body = body[..udpLength];
if (NetworkPackets.Read16(body, 6) != 0 && NetworkPackets.TransportChecksum(source, configuration.Address, 17, body) != 0) continue;
ReadOnlySpan<byte> answer = body[8..];
if (answer.Length < 2 || NetworkPackets.Read16(answer, 0) != dnsId) continue;
try { dnsAddress = DnsMessage.ReadAddress(answer, dnsId, dnsHost); }
catch (InvalidDataException error) { dnsError = "DNS validation failed: " + error.Message; }
}
}
internal void Released(TcpConnection connection) { if (ReferenceEquals(active, connection)) active = null; }
}
namespace XFESystem.Network;
/// <summary>
/// One-owner, bounded IPv4 client stack over real Ethernet. Static configuration,
/// ARP, UDP DNS and one active TCP stream; no host sockets or implicit fallback.
/// </summary>
public sealed class Ipv4Network
{
readonly IEthernetDevice device;
readonly INetworkPlatform platform;
readonly Ipv4Configuration configuration;
readonly byte[] receive = new byte[2048], transmit = new byte[1514], payload = new byte[1500];
readonly uint[] neighborAddresses = new uint[8];
readonly byte[][] neighborMacs = Enumerable.Range(0, 8).Select(_ => new byte[6]).ToArray();
int neighborCursor; uint awaitingNeighbor;
ushort ipId, dnsId, dnsPort;
string? dnsHost, dnsError;
uint dnsAddress;
TcpConnection? active;
public Ipv4Network(IEthernetDevice device, INetworkPlatform platform, Ipv4Configuration configuration)
{
this.device = device; this.platform = platform; this.configuration = configuration;
if (!NetworkPackets.IsUnicastMac(device.MacAddress) ||
configuration.Address == 0 || configuration.Gateway == 0 || configuration.DnsServer == 0)
throw new ArgumentException("The network requires a real unicast MAC and explicit IPv4/NAT configuration.");
}
internal long Now => platform.Milliseconds;
internal void Wait() => platform.Wait(1);
internal void Trace(string value) => platform.Trace(value);
internal static void CheckTimeout(long now, long deadline, string operation)
{ if (now >= deadline) throw new IOException(operation + " timed out."); }
internal long Deadline(int milliseconds)
{
if (milliseconds is < 1 or > 120_000) throw new ArgumentOutOfRangeException(nameof(milliseconds));
return checked(Now + milliseconds);
}
public Stream OpenTcp(string host, int port, int timeoutMilliseconds = 15_000)
=> OpenTcpUntil(host, port, Deadline(timeoutMilliseconds), timeoutMilliseconds);
public Stream OpenTcpUntil(string host, int port, long deadlineMilliseconds, int streamTimeoutMilliseconds = 15_000)
{
if (active != null) throw new InvalidOperationException("The one-owner network already has an active TCP stream.");
if (port is < 1 or > 65535) throw new ArgumentOutOfRangeException(nameof(port));
_ = Deadline(streamTimeoutMilliseconds);
long deadline = deadlineMilliseconds;
CheckTimeout(Now, deadline, "TCP open");
if (deadline - Now > 120000) throw new ArgumentOutOfRangeException(nameof(deadlineMilliseconds));
uint address = NetworkPackets.TryParseAddress(host, out uint literal) ? literal : Resolve(host, deadline);
if (address == 0 || address == configuration.Address || (address >> 28) >= 14)
throw new IOException("TCP destination is not a usable unicast IPv4 address.");
EnsureNeighbor(address, deadline);
ushort localPort = (ushort)(49152 + (platform.Random32() & 16383));
CheckTimeout(Now, deadline, "TCP open");
var connection = new TcpConnection(this, address, localPort, (ushort)port, platform.Random32(), streamTimeoutMilliseconds);
active = connection;
try { connection.Connect(deadline); return connection; }
catch { active = null; connection.Abort(deadline); throw; }
}
public uint Resolve(string host, int timeoutMilliseconds = 5000) => Resolve(host, Deadline(timeoutMilliseconds));
uint Resolve(string host, long deadline)
{
if (active != null) throw new InvalidOperationException("DNS cannot interrupt another active TCP owner.");
dnsHost = DnsMessage.NormalizeName(host); dnsId = (ushort)platform.Random32();
dnsPort = (ushort)(49152 + (platform.Random32() & 16383)); dnsAddress = 0; dnsError = null;
byte[] query = new byte[512]; int length = DnsMessage.WriteQuery(query, dnsId, dnsHost);
try
{
long nextSend = 0; int attempts = 0;
while (dnsAddress == 0)
{
CheckTimeout(Now, deadline, "DNS");
if (Now >= nextSend)
{
if (attempts++ >= 4) throw new IOException("DNS did not answer after bounded retransmission.");
SendUdp(configuration.DnsServer, dnsPort, 53, query.AsSpan(0, length), deadline);
nextSend = Now + Math.Min(1000, 250 << (attempts - 1));
}
Poll(deadline);
if (dnsError != null) throw new IOException(dnsError);
if (dnsAddress == 0) Wait();
}
Trace("XFE_NETWORK_DNS|Host|" + dnsHost + "|Address|" + NetworkPackets.Format(dnsAddress) + "\n");
return dnsAddress;
}
finally { dnsHost = null; }
}
uint NextHop(uint remote) => (remote & configuration.Mask) == (configuration.Address & configuration.Mask) ? remote : configuration.Gateway;
byte[]? FindNeighbor(uint address)
{
for (int i = 0; i < neighborAddresses.Length; i++) if (neighborAddresses[i] == address) return neighborMacs[i];
return null;
}
byte[] EnsureNeighbor(uint remote, long deadline)
{
uint address = NextHop(remote); byte[]? existing = FindNeighbor(address);
if (existing != null) return existing;
awaitingNeighbor = address;
try
{
long nextSend = 0; int attempts = 0;
while ((existing = FindNeighbor(address)) == null)
{
CheckTimeout(Now, deadline, "ARP");
if (Now >= nextSend)
{
if (attempts++ >= 4) throw new IOException("ARP next hop did not respond.");
SendArp(address, default, false, deadline); nextSend = Now + 250;
}
Poll(deadline); if (FindNeighbor(address) == null) Wait();
}
return existing;
}
finally { awaitingNeighbor = 0; }
}
void SendFrame(int length, long deadline)
{
CheckTimeout(Now, deadline, "Ethernet transmit");
int padded = Math.Max(length, 60);
transmit.AsSpan(length, padded - length).Clear();
if (!device.Send(transmit.AsSpan(0, padded), deadline)) throw new IOException("Ethernet transmit failed or timed out.");
CheckTimeout(Now, deadline, "Ethernet transmit");
}
void Ethernet(ReadOnlySpan<byte> destination, ushort protocol)
{
destination.CopyTo(transmit); device.MacAddress.CopyTo(transmit.AsSpan(6)); NetworkPackets.Write16(transmit, 12, protocol);
}
void SendArp(uint target, ReadOnlySpan<byte> targetMac, bool response, long deadline)
{
Span<byte> broadcast = stackalloc byte[6]; broadcast.Fill(255);
Ethernet(response ? targetMac : broadcast, 0x0806);
Span<byte> arp = transmit.AsSpan(14, 28); arp.Clear();
NetworkPackets.Write16(arp, 0, 1); NetworkPackets.Write16(arp, 2, 0x0800); arp[4] = 6; arp[5] = 4;
NetworkPackets.Write16(arp, 6, response ? (ushort)2 : (ushort)1);
device.MacAddress.CopyTo(arp[8..]); NetworkPackets.Write32(arp, 14, configuration.Address);
if (response) targetMac.CopyTo(arp[18..]); NetworkPackets.Write32(arp, 24, target);
SendFrame(42, deadline);
}
void ReceiveArp(ReadOnlySpan<byte> frame, long deadline)
{
if (frame.Length < 42) return;
ReadOnlySpan<byte> arp = frame[14..];
if (NetworkPackets.Read16(arp, 0) != 1 || NetworkPackets.Read16(arp, 2) != 0x0800 || arp[4] != 6 || arp[5] != 4 ||
!arp.Slice(8, 6).SequenceEqual(frame.Slice(6, 6)) || !NetworkPackets.IsUnicastMac(arp.Slice(8, 6)) ||
NetworkPackets.Read32(arp, 24) != configuration.Address) return;
uint sender = NetworkPackets.Read32(arp, 14); ushort operation = NetworkPackets.Read16(arp, 6);
if (operation == 2 && sender == awaitingNeighbor && arp.Slice(18, 6).SequenceEqual(device.MacAddress))
{
int slot = neighborCursor; neighborCursor = (neighborCursor + 1) % neighborAddresses.Length; neighborAddresses[slot] = sender;
arp.Slice(8, 6).CopyTo(neighborMacs[slot]);
Trace("XFE_NETWORK_ARP|NextHop|" + NetworkPackets.Format(sender) + "|Resolved|1\n");
}
if (operation == 1 && sender != 0) SendArp(sender, arp.Slice(8, 6), true, deadline);
}
void SendIpv4(uint destination, byte protocol, int length, ReadOnlySpan<byte> nextHopMac, long deadline)
{
if (length < 0 || length > 1480) throw new IOException("IPv4 payload exceeds the fixed Ethernet MTU.");
Ethernet(nextHopMac, 0x0800); Span<byte> ip = transmit.AsSpan(14, 20); ip.Clear();
ip[0] = 0x45; NetworkPackets.Write16(ip, 2, (ushort)(20 + length)); NetworkPackets.Write16(ip, 4, ++ipId);
NetworkPackets.Write16(ip, 6, 0x4000); ip[8] = 64; ip[9] = protocol;
NetworkPackets.Write32(ip, 12, configuration.Address); NetworkPackets.Write32(ip, 16, destination);
NetworkPackets.Write16(ip, 10, NetworkPackets.Checksum(ip)); payload.AsSpan(0, length).CopyTo(transmit.AsSpan(34));
SendFrame(34 + length, deadline);
}
void SendUdp(uint destination, ushort sourcePort, ushort destinationPort, ReadOnlySpan<byte> data, long deadline)
{
if (data.Length > 1472) throw new IOException("UDP datagram exceeds MTU.");
byte[] mac = EnsureNeighbor(destination, deadline); Span<byte> segment = payload.AsSpan(0, data.Length + 8); segment.Clear();
NetworkPackets.Write16(segment, 0, sourcePort); NetworkPackets.Write16(segment, 2, destinationPort);
NetworkPackets.Write16(segment, 4, (ushort)segment.Length); data.CopyTo(segment[8..]);
ushort checksum = NetworkPackets.TransportChecksum(configuration.Address, destination, 17, segment);
NetworkPackets.Write16(segment, 6, checksum == 0 ? (ushort)65535 : checksum);
SendIpv4(destination, 17, segment.Length, mac, deadline);
}
internal void SendTcp(uint destination, ushort sourcePort, ushort destinationPort, uint sequence, uint acknowledgement,
byte flags, ushort window, ReadOnlySpan<byte> data, long deadline, bool syn = false)
{
byte[] mac = FindNeighbor(NextHop(destination)) ?? throw new IOException("TCP next-hop ARP entry is unavailable.");
int header = syn ? 24 : 20;
if (data.Length > 1460 || header + data.Length > 1480) throw new IOException("TCP segment exceeds MTU.");
Span<byte> segment = payload.AsSpan(0, header + data.Length); segment.Clear();
NetworkPackets.Write16(segment, 0, sourcePort); NetworkPackets.Write16(segment, 2, destinationPort);
NetworkPackets.Write32(segment, 4, sequence); NetworkPackets.Write32(segment, 8, acknowledgement);
segment[12] = (byte)((header / 4) << 4); segment[13] = flags; NetworkPackets.Write16(segment, 14, window);
if (syn) { segment[20] = 2; segment[21] = 4; NetworkPackets.Write16(segment, 22, 1460); }
data.CopyTo(segment[header..]); NetworkPackets.Write16(segment, 16, NetworkPackets.TransportChecksum(configuration.Address, destination, 6, segment));
SendIpv4(destination, 6, segment.Length, mac, deadline);
}
internal void Poll(long deadline)
{
// A finite drain gives the scheduler/UI another chance even under flood.
for (int budget = 0; budget < 64; budget++)
{
CheckTimeout(Now, deadline, "Ethernet poll");
int length = device.Receive(receive);
CheckTimeout(Now, deadline, "Ethernet poll");
if (length == 0) return;
if (length < 14 || length > receive.Length) continue;
ReadOnlySpan<byte> frame = receive.AsSpan(0, length);
ushort type = NetworkPackets.Read16(frame, 12);
if (type == 0x0806) { ReceiveArp(frame, deadline); continue; }
if (type != 0x0800 || length < 34) continue;
ReadOnlySpan<byte> ip = frame[14..]; int header = (ip[0] & 15) * 4, total = NetworkPackets.Read16(ip, 2);
if ((ip[0] >> 4) != 4 || header < 20 || header > ip.Length || total < header || total > ip.Length ||
NetworkPackets.Checksum(ip[..header]) != 0 || (NetworkPackets.Read16(ip, 6) & 0x3fff) != 0 || ip[8] == 0 ||
NetworkPackets.Read32(ip, 16) != configuration.Address) continue;
uint source = NetworkPackets.Read32(ip, 12); byte protocol = ip[9]; ReadOnlySpan<byte> body = ip.Slice(header, total - header);
if (protocol == 6 && body.Length >= 20 && NetworkPackets.TransportChecksum(source, configuration.Address, 6, body) == 0)
active?.Receive(source, body, deadline);
if (protocol != 17 || dnsHost == null || source != configuration.DnsServer || body.Length < 8 ||
NetworkPackets.Read16(body, 0) != 53 || NetworkPackets.Read16(body, 2) != dnsPort) continue;
int udpLength = NetworkPackets.Read16(body, 4);
if (udpLength < 8 || udpLength > body.Length) continue;
body = body[..udpLength];
if (NetworkPackets.Read16(body, 6) != 0 && NetworkPackets.TransportChecksum(source, configuration.Address, 17, body) != 0) continue;
ReadOnlySpan<byte> answer = body[8..];
if (answer.Length < 2 || NetworkPackets.Read16(answer, 0) != dnsId) continue;
try { dnsAddress = DnsMessage.ReadAddress(answer, dnsId, dnsHost); }
catch (InvalidDataException error) { dnsError = "DNS validation failed: " + error.Message; }
}
}
internal void Released(TcpConnection connection) { if (ReferenceEquals(active, connection)) active = null; }
}