diff --git a/Magma.sln b/Magma.sln
index de00e04..545f52b 100644
--- a/Magma.sln
+++ b/Magma.sln
@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Performance", "benchm
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.NetMap.TcpHost", "sample\Magma.NetMap.TcpHost\Magma.NetMap.TcpHost.csproj", "{358CD7A8-E78B-4DCD-8640-EA62442755D7}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.NetMap.UdpEchoServer", "sample\Magma.NetMap.UdpEchoServer\Magma.NetMap.UdpEchoServer.csproj", "{F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.Internet.Ip.Facts", "test\Magma.Internet.Ip.Facts\Magma.Internet.Ip.Facts.csproj", "{47D45F49-E8E0-4267-9FDA-8635DA6F5348}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Magma.PCap", "src\Magma.PCap\Magma.PCap.csproj", "{F91B0866-9DF5-488D-AD3E-1E471509C60F}"
@@ -235,6 +237,18 @@ Global
{358CD7A8-E78B-4DCD-8640-EA62442755D7}.Release|x64.Build.0 = Release|Any CPU
{358CD7A8-E78B-4DCD-8640-EA62442755D7}.Release|x86.ActiveCfg = Release|Any CPU
{358CD7A8-E78B-4DCD-8640-EA62442755D7}.Release|x86.Build.0 = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|x64.Build.0 = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Debug|x86.Build.0 = Debug|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|x64.ActiveCfg = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|x64.Build.0 = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|x86.ActiveCfg = Release|Any CPU
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B}.Release|x86.Build.0 = Release|Any CPU
{47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47D45F49-E8E0-4267-9FDA-8635DA6F5348}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -326,6 +340,7 @@ Global
{D8E37D7D-E535-4790-B93C-1E60AD624C70} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19}
{C2C87D71-4BB3-4DD1-BE4D-35DEBF927290} = {0581267E-B53B-4BBE-BB90-80C3CA0C4ACD}
{358CD7A8-E78B-4DCD-8640-EA62442755D7} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0}
+ {F8B5E3A1-2D4C-4F8A-9E5B-1C3D4E5F6A7B} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0}
{47D45F49-E8E0-4267-9FDA-8635DA6F5348} = {B1BA53C8-CCCF-46D5-BA9F-6031811F2E19}
{F91B0866-9DF5-488D-AD3E-1E471509C60F} = {34A1DC50-486C-4BB9-9929-1805CA0B0DD0}
{76D4F5E7-2C04-420D-A43A-B721D2F32904} = {23E375E0-8A4A-4D6A-8C96-9F2046CE9EB0}
diff --git a/sample/Magma.NetMap.UdpEchoServer/Magma.NetMap.UdpEchoServer.csproj b/sample/Magma.NetMap.UdpEchoServer/Magma.NetMap.UdpEchoServer.csproj
new file mode 100644
index 0000000..0591053
--- /dev/null
+++ b/sample/Magma.NetMap.UdpEchoServer/Magma.NetMap.UdpEchoServer.csproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net10.0
+ true
+ latest
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/Magma.NetMap.UdpEchoServer/Program.cs b/sample/Magma.NetMap.UdpEchoServer/Program.cs
new file mode 100644
index 0000000..f8b7e63
--- /dev/null
+++ b/sample/Magma.NetMap.UdpEchoServer/Program.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using Magma.Internet.Ip;
+using Magma.Network.Header;
+
+namespace Magma.NetMap.UdpEchoServer
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ var listenPort = (ushort)7777;
+
+ if (args.Length >= 1)
+ {
+ if (ushort.TryParse(args[0], out var port))
+ {
+ listenPort = port;
+ }
+ }
+
+ Console.WriteLine("=== Magma UDP Echo Server Sample ===");
+ Console.WriteLine($"Listen Port: {listenPort}");
+ Console.WriteLine($"Ethernet Header: {Unsafe.SizeOf()} bytes");
+ Console.WriteLine($"IPv4 Header: {Unsafe.SizeOf()} bytes");
+ Console.WriteLine($"UDP Header: {Unsafe.SizeOf()} bytes");
+ Console.WriteLine();
+ Console.WriteLine("This sample demonstrates:");
+ Console.WriteLine(" - UDP header parsing using Magma.Transport.Udp");
+ Console.WriteLine(" - End-to-end packet processing (Ethernet -> IPv4 -> UDP)");
+ Console.WriteLine(" - Zero-copy packet manipulation with Span");
+ Console.WriteLine();
+ Console.WriteLine("To run this sample with NetMap:");
+ Console.WriteLine(" 1. Ensure NetMap kernel module is loaded");
+ Console.WriteLine(" 2. Run with sudo or appropriate capabilities");
+ Console.WriteLine(" 3. Specify network interface: dotnet run -- eth0");
+ Console.WriteLine();
+ Console.WriteLine("Example UDP packet processing:");
+ Console.WriteLine();
+
+ DemonstrateUdpParsing(listenPort);
+
+ Console.WriteLine();
+ Console.WriteLine("Sample completed. See README.md for integration with NetMap.");
+ }
+
+ static unsafe void DemonstrateUdpParsing(ushort listenPort)
+ {
+ var packet = CreateSampleUdpPacket(listenPort);
+
+ Console.WriteLine($"Sample packet ({packet.Length} bytes):");
+ Console.WriteLine(BitConverter.ToString(packet));
+ Console.WriteLine();
+
+ if (Ethernet.TryConsume(packet, out var ethernet, out var etherData))
+ {
+ Console.WriteLine(ethernet.ToString());
+
+ if (ethernet.Ethertype == EtherType.IPv4)
+ {
+ if (IPv4.TryConsume(etherData, out var ipv4, out var ipData))
+ {
+ Console.WriteLine(ipv4.ToString());
+
+ if (ipv4.Protocol == ProtocolNumber.Udp)
+ {
+ if (Udp.TryConsume(ipData, out var udp, out var udpData))
+ {
+ Console.WriteLine(udp.ToString());
+ Console.WriteLine($"+- UDP Data -------------------------------------------------------------------+");
+ Console.WriteLine($"| Length: {udpData.Length} bytes".PadRight(87) + "|");
+ if (udpData.Length > 0)
+ {
+ var preview = udpData.Length > 60 ? udpData.Slice(0, 60) : udpData;
+ Console.WriteLine($"| {BitConverter.ToString(preview.ToArray())}".PadRight(87) + "|");
+ }
+ Console.WriteLine($"+------------------------------------------------------------------------------+");
+ Console.WriteLine();
+ Console.WriteLine($"✓ Successfully parsed UDP packet to port {udp.DestinationPort}");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static byte[] CreateSampleUdpPacket(ushort destPort)
+ {
+ var packet = new byte[64];
+ var span = packet.AsSpan();
+
+ ref byte current = ref MemoryMarshal.GetReference(span);
+
+ ref var ethernet = ref Unsafe.As(ref current);
+ ethernet.Destination = new MacAddress(0x00, 0x11, 0x22, 0x33, 0x44, 0x55);
+ ethernet.Source = new MacAddress(0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF);
+ ethernet.Ethertype = EtherType.IPv4;
+
+ current = ref Unsafe.Add(ref current, Unsafe.SizeOf());
+
+ ref var ipv4 = ref Unsafe.As(ref current);
+ ipv4.VersionAndHeaderLength = 0x45;
+ ipv4.TypeOfService = 0;
+ ipv4.TotalLength = (ushort)(20 + 8 + 10);
+ ipv4.Identification = 12345;
+ ipv4.FlagsAndFragmentOffset = 0;
+ ipv4.TimeToLive = 64;
+ ipv4.Protocol = ProtocolNumber.Udp;
+ ipv4.SourceAddress = new IPv4Address(192, 168, 1, 100);
+ ipv4.DestinationAddress = new IPv4Address(192, 168, 1, 1);
+ ipv4.HeaderChecksum = 0;
+
+ current = ref Unsafe.Add(ref current, Unsafe.SizeOf());
+
+ ref var udp = ref Unsafe.As(ref current);
+ udp.SourcePort = 12345;
+ udp.DestinationPort = destPort;
+ udp.Length = (ushort)(8 + 10);
+ udp.Checksum = 0;
+
+ current = ref Unsafe.Add(ref current, Unsafe.SizeOf());
+
+ var data = "Hello UDP!"u8.ToArray();
+ data.AsSpan().CopyTo(MemoryMarshal.CreateSpan(ref current, data.Length));
+
+ return packet;
+ }
+ }
+}
diff --git a/sample/Magma.NetMap.UdpEchoServer/README.md b/sample/Magma.NetMap.UdpEchoServer/README.md
new file mode 100644
index 0000000..c674de7
--- /dev/null
+++ b/sample/Magma.NetMap.UdpEchoServer/README.md
@@ -0,0 +1,154 @@
+# Magma UDP Echo Server Sample
+
+A simple UDP echo server demonstrating end-to-end usage of the Magma network stack with UDP transport.
+
+## Overview
+
+This sample application shows how to:
+- Parse incoming UDP packets using the Magma stack (Ethernet → IPv4 → UDP)
+- Process UDP datagrams at the transport layer
+- Construct and transmit UDP response packets
+- Use NetMap for high-performance packet I/O on Linux
+
+The server listens on a specified UDP port and echoes back any received data to the sender.
+
+## What It Demonstrates
+
+- **UDP Header Parsing**: Uses `Udp.TryConsume()` to parse UDP headers from raw packet data
+- **Packet Construction**: Builds outgoing UDP packets from scratch, including:
+ - Ethernet frame with swapped MAC addresses
+ - IPv4 header with swapped IP addresses and checksum calculation
+ - UDP header with swapped ports
+- **Zero-Copy Processing**: Direct manipulation of packet buffers using `Span` and unsafe pointers
+- **NetMap Integration**: High-performance packet I/O using Linux NetMap interface
+
+## Building
+
+From the repository root:
+
+```bash
+dotnet build sample/Magma.NetMap.UdpEchoServer
+```
+
+## Running
+
+### Prerequisites
+
+- Linux with NetMap kernel module loaded
+- Network interface available for NetMap (typically a dedicated interface)
+- Root privileges or appropriate capabilities
+
+### Start the Server
+
+```bash
+# Run with default settings (eth0, port 7777)
+sudo dotnet run --project sample/Magma.NetMap.UdpEchoServer
+
+# Specify interface and port
+sudo dotnet run --project sample/Magma.NetMap.UdpEchoServer -- eth1 8888
+```
+
+### Test the Server
+
+From another machine on the same network:
+
+```bash
+# Using netcat (nc)
+echo "Hello Magma!" | nc -u 7777
+
+# Using Python
+python3 -c "import socket; s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM); s.sendto(b'Hello', ('', 7777)); print(s.recvfrom(1024))"
+
+# Using socat
+echo "Test message" | socat - UDP4::7777
+```
+
+## Architecture
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ Application │
+│ (UdpEchoReceiver) │
+└────────────────────┬────────────────────────────────────┘
+ │
+ │ IPacketReceiver.TryConsume()
+ │
+┌────────────────────▼────────────────────────────────────┐
+│ NetMap Transport │
+│ (NetMapPort) │
+└────────────────────┬────────────────────────────────────┘
+ │
+ │ Direct packet I/O
+ │
+┌────────────────────▼────────────────────────────────────┐
+│ Network Interface │
+│ (NetMap ring) │
+└─────────────────────────────────────────────────────────┘
+```
+
+## Code Flow
+
+### Receive Path
+
+1. NetMap delivers raw packet buffer to `UdpEchoReceiver.TryConsume()`
+2. Parse Ethernet frame: `Ethernet.TryConsume()`
+3. Check for IPv4: `etherIn.Ethertype == EtherType.IPv4`
+4. Parse IPv4 header: `IPv4.TryConsume()`
+5. Check for UDP: `ipIn.Protocol == ProtocolNumber.Udp`
+6. Parse UDP header: `Udp.TryConsume()`
+7. Check destination port matches listen port
+
+### Transmit Path
+
+1. Get transmit buffer: `_transmitter.TryGetNextBuffer()`
+2. Build Ethernet header (swap source/dest MACs)
+3. Build IPv4 header (swap source/dest IPs, calculate checksum)
+4. Build UDP header (swap source/dest ports)
+5. Copy original data payload
+6. Send packet: `_transmitter.SendBuffer()` and `ForceFlush()`
+
+## Technical Details
+
+### UDP Header Structure
+
+The `Udp` struct in `Magma.Transport.Udp` represents the 8-byte UDP header:
+
+```
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Source Port | Destination Port |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| Length | Checksum |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+```
+
+- **Source/Destination Port**: 16-bit port numbers
+- **Length**: Total length of UDP header + data (minimum 8 bytes)
+- **Checksum**: Optional error checking (set to 0 in this sample)
+
+### Performance Characteristics
+
+- **Zero-copy**: Direct buffer manipulation using `Unsafe` and `Span`
+- **Minimal allocations**: Packet buffers are pooled by NetMap
+- **Kernel bypass**: NetMap bypasses the Linux network stack for lower latency
+
+## Limitations
+
+- **NetMap only**: Requires Linux with NetMap kernel module
+- **No checksum validation**: UDP checksum is not validated on receive
+- **No fragmentation**: Only handles packets that fit in a single Ethernet frame
+- **Simplified error handling**: Production code would need more robust error checking
+
+## Related Components
+
+- `src/Magma.Transport.Udp` - UDP protocol implementation
+- `src/Magma.Internet.Ip` - IPv4/IPv6 headers
+- `src/Magma.Link` - Ethernet frame handling
+- `src/Magma.NetMap` - NetMap transport integration
+
+## See Also
+
+- [RFC 768 - User Datagram Protocol](https://www.rfc-editor.org/rfc/rfc768)
+- [NetMap - Fast packet I/O framework](http://info.iet.unipi.it/~luigi/netmap/)
+- Other samples: `sample/Magma.NetMap.TcpHost`, `samples/Magma.NetMap.Host`
diff --git a/src/Magma.Transport.Udp/Header/Udp.cs b/src/Magma.Transport.Udp/Header/Udp.cs
index 8a3cadb..07cfade 100644
--- a/src/Magma.Transport.Udp/Header/Udp.cs
+++ b/src/Magma.Transport.Udp/Header/Udp.cs
@@ -1,8 +1,82 @@
using System;
+using System.Net;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace Magma.Network.Header
{
+ ///
+ /// Represents a UDP header as defined in RFC 768.
+ /// UDP header is 8 bytes: Source Port (2), Destination Port (2), Length (2), Checksum (2)
+ ///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Udp
{
+ private ushort _sourcePort;
+ private ushort _destinationPort;
+ private ushort _length;
+ private ushort _checksum;
+
+ ///
+ /// Source port number.
+ ///
+ public ushort SourcePort
+ {
+ get => (ushort)IPAddress.NetworkToHostOrder((short)_sourcePort);
+ set => _sourcePort = (ushort)IPAddress.HostToNetworkOrder((short)value);
+ }
+
+ ///
+ /// Destination port number.
+ ///
+ public ushort DestinationPort
+ {
+ get => (ushort)IPAddress.NetworkToHostOrder((short)_destinationPort);
+ set => _destinationPort = (ushort)IPAddress.HostToNetworkOrder((short)value);
+ }
+
+ ///
+ /// Length in bytes of the UDP header and data.
+ /// Minimum value is 8 (header only).
+ ///
+ public ushort Length
+ {
+ get => (ushort)IPAddress.NetworkToHostOrder((short)_length);
+ set => _length = (ushort)IPAddress.HostToNetworkOrder((short)value);
+ }
+
+ ///
+ /// Checksum for error-checking of the header and data.
+ ///
+ public ushort Checksum
+ {
+ get => (ushort)IPAddress.NetworkToHostOrder((short)_checksum);
+ set => _checksum = (ushort)IPAddress.HostToNetworkOrder((short)value);
+ }
+
+ ///
+ /// Attempts to parse a UDP header from the input span.
+ ///
+ /// Input byte span containing UDP packet
+ /// Parsed UDP header
+ /// Remaining data after the UDP header
+ /// True if parsing succeeded, false otherwise
+ public static bool TryConsume(ReadOnlySpan input, out Udp udp, out ReadOnlySpan data)
+ {
+ if (input.Length >= Unsafe.SizeOf())
+ {
+ udp = Unsafe.As(ref MemoryMarshal.GetReference(input));
+ data = input.Slice(Unsafe.SizeOf());
+ return true;
+ }
+
+ udp = default;
+ data = default;
+ return false;
+ }
+
+ public override string ToString() =>
+ "+- UDP Datagram -----------------------------------------------------------------------+" + Environment.NewLine +
+ $"| :{SourcePort.ToString()} -> :{DestinationPort.ToString()} Length: {Length} Checksum: 0x{Checksum:X4}".PadRight(87) + "|";
}
}