I am trying to implement an API that uses raw sockets to send data. I want to be able to specify all of the content in a packet, from the ethernet headers to the packet payload.
This document outlines the binary protocol that I am trying to implement: docs/saprus_proto_design.md · main · C2 Games / Red Team / Saprus · GitLab
This function is a basic example of how they create a raw socket and send data in the existing implementation of the protocol: utils/relay_message.go · dev · C2 Games / Red Team / Saprus · GitLab
Here is what I have tried doing using std.Io.net:
const ip: std.Io.net.IpAddress = .{ .ip4 = .unspecified(0) };
const socket = try ip.bind(init.io, .{ .mode = .raw, .protocol = .ethernet }); // I have also tried .protocol = .raw and gotten the same behavior.
defer socket.close(init.io);
try socket.send(init.io, &.{ .ip4 = try .parse("255.255.255.255", 8888) }, "foo");
Where I have foo I want to write data starting with the ethernet headers, then the IP headers, then the UDP headers, then my payload data. If I run my program as a non-root user, the program fails at the try ip.bind(...), which is what I would expect and what I want. If I run as root, I fail with the following stack trace:
unexpected errno: 13
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/posix.zig:2216:40: 0x10645e0 in unexpectedErrno (std.zig)
std.debug.dumpCurrentStackTrace(.{});
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/Io/Threaded.zig:11305:63: 0x11bfb46 in netSendMany (std.zig)
else => |err| return posix.unexpectedErrno(err),
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/Io/Threaded.zig:11099:29: 0x11b3c7f in netSendPosix (std.zig)
i += netSendMany(handle, messages[i..], posix_flags) catch |err| return .{ err, i };
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/Io/net.zig:1101:47: 0x11a7e72 in send (std.zig)
const err, const n = io.vtable.netSend(io.userdata, s.handle, (&message)[0..1], .{});
^
/home/robby/src/zaprus/src/main.zig:100:20: 0x11a35ce in main (main.zig)
try socket.send(init.io, &.{ .ip4 = try .parse("255.255.255.255", 8888) }, "foo");
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/start.zig:718:30: 0x11a43eb in callMain (std.zig)
return wrapMain(root.main(.{
^
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/start.zig:190:5: 0x11a2a81 in _start (std.zig)
asm volatile (switch (native_arch) {
^
error: Unexpected
/home/robby/downloads/zig-x86_64-linux-0.16.0-dev.2223+4f16e80ce/lib/std/Io/net.zig:1102:21: 0x11a7f0e in send (std.zig)
if (n != 1) return err.?;
^
/home/robby/src/zaprus/src/main.zig:100:5: 0x11a365f in main (main.zig)
try socket.send(init.io, &.{ .ip4 = try .parse("255.255.255.255", 8888) }, "foo");
^
(errno 13 is std.os.linux.E.ACCES (Permission denied), which should probably be an expected error)
Am I trying to do something that is simply unsupported by the current Io interface? Or am I misunderstanding how to do what I am trying to do? It seems like, instead of specifying an IP address and port that is not actually used, I should be able to get a raw socket by specifying the std.Io.net.Interface, but there does not seem to be any way to do that right now.