Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Netlink socket protocols are largely actually socket options, which are also missing #1252

Open
DianaNites opened this issue Jan 6, 2025 · 3 comments

Comments

@DianaNites
Copy link

https://docs.rs/rustix/0.38.42/rustix/net/sockopt/index.html

Many of the supposed netlink protocols are in reality netlink specific socket options, which are also completely missing in the sockopt module

https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.ADD_MEMBERSHIP.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.DROP_MEMBERSHIP.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.LIST_MEMBERSHIPS.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.BROADCAST_ERROR.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.NO_ENOBUFS.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.CAP_ACK.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.EXT_ACK.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.GET_STRICT_CHK.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.LISTEN_ALL_NSID.html
https://docs.rs/rustix/0.38.42/rustix/net/netlink/constant.PKTINFO.html

all of these are socket options described in netlink.7 or the netlink handbook.


Additionally, when I read rustix was low-level and "aims to provide safe and idiomatic Rust interfaces to low-level syscalls", I was surprised to find no direct interface to many syscalls, but instead high level wrappers exclusively exposing specific subsets of them that rustix knows about, and nothing for those it doesn't.

I was expecting a lower level API to syscalls like this, with all the benefits of type, memory, and I/O safety rustix provides over the raw syscalls and bare integers, but that doesn't seem to be a design goal for rustix.

@sunfishcode
Copy link
Member

I don't know how it could be possible to expose a lower-level API while still having any of the benefits of type, memory, or I/O safety. To provide a safe interface, rustix has to know which arguments are pointers, which are file descriptors, what memory is to be mutated, and so on.

Rustix once attempted to expose only typed interfaces to ioctls for this reason, however there are just too many ioctls for it to be practical to cover them all, so Rustix now exposes a generic ioctl function. I've been resisting doing this for exotic socket features, but there have been a lot of requests for a lot of various socket features, so I've now changed my mind.

It's telling that the very first sentence in the netlink book describes netlink as an ioctl replacement.

I would like to add a feature to Rustix that just exposes raw socket features as an unsafe interface. We can still add safe interfaces for features as we're able to, but the unsafe interface will be there for everything else. I don't yet know what that should look like, and would appreciate any help anyone might be able to offer.

@DianaNites
Copy link
Author

DianaNites commented Jan 6, 2025

To provide a safe interface, rustix has to know which arguments are pointers, which are file descriptors, what memory is to be mutated, and so on.

Unless I'm mistaken, all of this is known in advance and perfectly safe for at least setsockopt/getsockopt, which will also safely return errors for invalid options, levels, and values?

AIUI socket options are at least always plain old data, so in my mind this can be a perfectly safe interface taking opaque bytes, unless theres some evil socket options that modify optval on setsockopt it can always be immutable(despite it being defined with const void in the man page), and getsockopt is always mutable, with construction/buffer sizing left to the caller and perhaps helpers provided by rustix for the times when it isnt just a boolean int?(e.g. IP_ADD_MEMBERSHIP).

This leaves all the benefits of the errno handling, I/O safety for the passed FD, memory safety for the buffers, type safety for the levels and option names, with corresponding Raw types and from_raw functions. Protocol::from_raw is safe and these could be too?

The levels and option names can even be paired into some sort of SocketOption type. Something like(pseudo-code)

struct SocketOption {
    level: SocketOptionLevel, // i32
    name: SocketOptionName, // i32
}

pub fn setsockopt(fd: &BorrowedFd, opt: SocketOption, value: &[u8]) -> Result<()>;
pub fn getsockopt(fd: &BorrowedFd, opt: SocketOption, value: &mut [u8]) -> Result<()>;

mod netlink {
    pub const CAP_ACK: SocketOption = SocketOption::from_raw(SOL_NETLINK, NETLINK_CAP_ACK);
}

// usage:
setsockopt(&socket, netlink::CAP_ACK, &0u32.to_ne_bytes()[..]);

This is still low level in the sense of being a straight-forward wrapper around the direct syscall which the caller still needs to know how to use, but is perfectly safe in rust terms and lagely benefits from the various other safeties(type, I/O) and clear intent signaling of rustix, and can form the basis of a higher level wrapper that knows about the option/value pairing from rustix or other crates.(I don't think its rustix style, but I like the idea of type-state generics for this.)

@sunfishcode
Copy link
Member

That's a good point. And I guess it's ok even if Linux adds arbitrary new types in the future, because users still need an unsafe block to convert from bytes into pointers or OwnedFds or whatever else. So this sounds like a good direction to go in.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants