mirror of
https://git.robbyzambito.me/zaprus
synced 2026-02-04 08:24:52 +00:00
110 lines
3.4 KiB
Zig
110 lines
3.4 KiB
Zig
// 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;
|