namespace XFESystem.Network; /// Actual Ethernet frames without preamble/FCS; implementations own DMA and never fabricate packets. public interface IEthernetDevice { ReadOnlySpan MacAddress { get; } // Absolute deadline in the same monotonic millisecond domain as INetworkPlatform. // Implementations must not install a separate transmit timeout. bool Send(ReadOnlySpan frame, long deadlineMilliseconds); int Receive(Span frame); } public interface INetworkPlatform { long Milliseconds { get; } void Wait(int milliseconds); uint Random32(); void Trace(string text); } /// Explicit static configuration, not a DHCP lease or physical-network discovery. public readonly record struct Ipv4Configuration(uint Address, uint Mask, uint Gateway, uint DnsServer) { public static Ipv4Configuration QemuUserNat => new(0x0a00020f, 0xffffff00, 0x0a000202, 0x0a000203); } public static class NetworkPackets { public static bool IsUnicastMac(ReadOnlySpan value) { if (value.Length != 6 || (value[0] & 1) != 0) return false; byte any = 0; foreach (byte octet in value) any |= octet; return any != 0; } public static ushort Read16(ReadOnlySpan value, int offset) => (ushort)((value[offset] << 8) | value[offset + 1]); public static uint Read32(ReadOnlySpan value, int offset) => ((uint)Read16(value, offset) << 16) | Read16(value, offset + 2); public static void Write16(Span value, int offset, ushort number) { value[offset] = (byte)(number >> 8); value[offset + 1] = (byte)number; } public static void Write32(Span value, int offset, uint number) { Write16(value, offset, (ushort)(number >> 16)); Write16(value, offset + 2, (ushort)number); } public static string Format(uint value) => $"{value >> 24}.{(value >> 16) & 255}.{(value >> 8) & 255}.{value & 255}"; public static bool TryParseAddress(string text, out uint address) { address = 0; string[] parts = text.Split('.'); if (parts.Length != 4) return false; foreach (string part in parts) { if (part.Length is 0 or > 3 || part.Any(c => c is < '0' or > '9') || !uint.TryParse(part, out uint octet) || octet > 255) { address = 0; return false; } address = (address << 8) | octet; } return true; } static uint AddWords(ReadOnlySpan bytes, uint sum) { for (int i = 0; i + 1 < bytes.Length; i += 2) sum += Read16(bytes, i); if ((bytes.Length & 1) != 0) sum += (uint)bytes[^1] << 8; return sum; } static ushort Finish(uint sum) { while ((sum >> 16) != 0) sum = (sum & 65535) + (sum >> 16); return (ushort)~sum; } public static ushort Checksum(ReadOnlySpan bytes) => Finish(AddWords(bytes, 0)); public static ushort TransportChecksum(uint source, uint destination, byte protocol, ReadOnlySpan bytes) => Finish(AddWords(bytes, (source >> 16) + (source & 65535) + (destination >> 16) + (destination & 65535) + protocol + (uint)bytes.Length)); }