1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
|
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;
|