diff options
Diffstat (limited to 'src/EthIpUdp.zig')
| -rw-r--r-- | src/EthIpUdp.zig | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/EthIpUdp.zig b/src/EthIpUdp.zig new file mode 100644 index 0000000..251ed64 --- /dev/null +++ b/src/EthIpUdp.zig @@ -0,0 +1,109 @@ +// Copyright 2026 Robby Zambito +// +// This file is part of zaprus. +// +// Zaprus is free software: you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the Free Software +// Foundation, either version 3 of the License, or (at your option) any later +// version. +// +// Zaprus is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +// A PARTICULAR PURPOSE. See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with +// Zaprus. If not, see <https://www.gnu.org/licenses/>. + +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; |
