pub const EthIpUdp = packed struct(u336) { // 42 bytes * 8 bits = 336 // --- UDP (Last in memory, defined first for LSB->MSB) --- udp: packed struct { checksum: u16 = 0, len: u16, dst_port: u16, src_port: u16, }, // --- IP --- ip: packed struct { dst_addr: u32, src_addr: u32, header_checksum: u16 = 0, protocol: u8 = 17, // udp ttl: u8 = 0x40, // fragment_offset (13 bits) + flags (3 bits) = 16 bits // In Big Endian, flags are the high bits of the first byte. // To have flags appear first in the stream, define them last here. fragment_offset: u13 = 0, flags: packed struct(u3) { reserved: u1 = 0, dont_fragment: u1 = 1, more_fragments: u1 = 0, } = .{}, id: u16, len: u16, tos: u8 = undefined, // ip_version (4 bits) + ihl (4 bits) = 8 bits // To have version appear first (high nibble), define it last. ihl: u4 = 5, ip_version: u4 = 4, }, // --- Ethernet --- eth_type: u16 = std.os.linux.ETH.P.IP, src_mac: @Vector(6, u8), dst_mac: @Vector(6, u8) = @splat(0xff), pub fn toBytes(self: @This()) [336 / 8]u8 { var res: [336 / 8]u8 = undefined; var w: Writer = .fixed(&res); w.writeStruct(self, .big) catch unreachable; return res; } pub fn setPayloadLen(self: *@This(), len: usize) void { self.ip.len = @intCast(len + (@bitSizeOf(@TypeOf(self.udp)) / 8) + (@bitSizeOf(@TypeOf(self.ip)) / 8)); // Zero the checksum field before calculation self.ip.header_checksum = 0; // Serialize IP header to big-endian bytes var ip_bytes: [@bitSizeOf(@TypeOf(self.ip)) / 8]u8 = undefined; var w: Writer = .fixed(&ip_bytes); w.writeStruct(self.ip, .big) catch unreachable; // Calculate checksum over serialized bytes self.ip.header_checksum = onesComplement16(&ip_bytes); self.udp.len = @intCast(len + (@bitSizeOf(@TypeOf(self.udp)) / 8)); } }; fn onesComplement16(data: []const u8) u16 { var sum: u32 = 0; // Process pairs of bytes as 16-bit words var i: usize = 0; while (i + 1 < data.len) : (i += 2) { const word: u16 = (@as(u16, data[i]) << 8) | data[i + 1]; sum += word; } // Handle odd byte if present if (data.len % 2 == 1) { sum += @as(u32, data[data.len - 1]) << 8; } // Fold 32-bit sum to 16 bits while (sum >> 16 != 0) { sum = (sum & 0xFFFF) + (sum >> 16); } // Return ones' complement return ~@as(u16, @truncate(sum)); } const std = @import("std"); const Writer = std.Io.Writer;