This patch series is intended to create Rust abstractions for Sockets
and other fundamental network entities.
Specifically, it was added:
- Ip address and Socket address wrappers (for `in_addr`, `in6_addr`,
`sockaddr_in`, `sockaddr_in6`, `sockaddr_storage`).
- Socket wrapper.
- Socket flags and options enums.
- TCP and UDP specific abstractions over the Rust Socket structure.
This series is a RFC because I would appreciate some feedback about:
- The structure of the module: is the division of the files and modules
appropriate or should it be more or less fine-grained?
Also, should the `net` module export all the structures of its
submodules? I noticed that it is done in the standard library.
- Whether the documentation is comprehensive enough.
- A few other specific questions, written in the individual patches.
I would greatly appreciate any kind of feedback or opinion.
I am pretty new to the patch/mailing list world, so please point out any
mistake I might make.
The changes in this patch series are based on top of the latest commit
of `rust-next` in the Rust git tree:
19cd7b5d229c ("btf, scripts: rust: drop is_rust_module.sh")
Michele Dalle Rive (7):
rust: net: add net module files and shared enums.
rust: net: add ip and socket address bindings.
rust: net: add socket-related flags and flagset.
rust: net: add socket wrapper.
rust: net: implement socket options API.
rust: net: add socket TCP wrappers.
rust: net: add socket UDP wrappers.
rust/bindings/bindings_helper.h | 3 +
rust/kernel/lib.rs | 2 +
rust/kernel/net.rs | 185 +++++
rust/kernel/net/addr.rs | 1215 ++++++++++++++++++++++++++++++
rust/kernel/net/ip.rs | 73 ++
rust/kernel/net/socket.rs | 641 ++++++++++++++++
rust/kernel/net/socket/flags.rs | 467 ++++++++++++
rust/kernel/net/socket/opts.rs | 1222 +++++++++++++++++++++++++++++++
rust/kernel/net/tcp.rs | 252 +++++++
rust/kernel/net/udp.rs | 182 +++++
10 files changed, 4242 insertions(+)
create mode 100644 rust/kernel/net.rs
create mode 100644 rust/kernel/net/addr.rs
create mode 100644 rust/kernel/net/ip.rs
create mode 100644 rust/kernel/net/socket.rs
create mode 100644 rust/kernel/net/socket/flags.rs
create mode 100644 rust/kernel/net/socket/opts.rs
create mode 100644 rust/kernel/net/tcp.rs
create mode 100644 rust/kernel/net/udp.rs
--
2.41.0
Add `TcpListener` and `TcpStream` wrappers around the Rust Socket.
They provide a convenient way to handle TCP sockets.
This interface is intended to be as close as possible to the one in `std::net`.
Signed-off-by: Michele Dalle Rive <[email protected]>
---
rust/kernel/net.rs | 1 +
rust/kernel/net/tcp.rs | 252 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 253 insertions(+)
create mode 100644 rust/kernel/net/tcp.rs
diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs
index 7d58ebb0324f..c7d9d4b0bcab 100644
--- a/rust/kernel/net.rs
+++ b/rust/kernel/net.rs
@@ -13,6 +13,7 @@
pub mod addr;
pub mod ip;
pub mod socket;
+pub mod tcp;
/// The address family.
///
diff --git a/rust/kernel/net/tcp.rs b/rust/kernel/net/tcp.rs
new file mode 100644
index 000000000000..86a42ac3e367
--- /dev/null
+++ b/rust/kernel/net/tcp.rs
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! TCP socket wrapper.
+//!
+//! This module contains wrappers for a TCP Socket ([`TcpListener`]) and an active
+//! TCP connection ([`TcpStream`]).
+//! The wrappers are just convenience structs around the generic [`Socket`] type.
+//!
+//! The API is inspired by the Rust standard library's [`TcpListener`](https://doc.rust-lang.org/std/net/struct.TcpListener.html) and [`TcpStream`](https://doc.rust-lang.org/std/net/struct.TcpStream.html).
+
+use crate::error::Result;
+use crate::net::addr::SocketAddr;
+use crate::net::ip::IpProtocol;
+use crate::net::socket::flags::{FlagSet, ReceiveFlag, SendFlag};
+use crate::net::socket::opts::{SocketOption, WritableOption};
+use crate::net::socket::{ShutdownCmd, SockType, Socket};
+use crate::net::AddressFamily;
+use kernel::net::socket::MessageHeader;
+
+/// A TCP listener.
+///
+/// Wraps the [`Socket`] type to create a TCP-specific interface.
+///
+/// The wrapper abstracts away the generic Socket methods that a connection-oriented
+/// protocol like TCP does not need.
+///
+/// # Examples
+/// ```rust
+/// use kernel::net::tcp::TcpListener;
+/// use kernel::net::addr::*;
+///
+/// let listener = TcpListener::new(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000))).unwrap();
+/// while let Ok(stream) = listener.accept() {
+/// // ...
+/// }
+pub struct TcpListener(pub(crate) Socket);
+
+impl TcpListener {
+ /// Create a new TCP listener bound to the given address.
+ ///
+ /// The listener will be ready to accept connections.
+ pub fn new(address: SocketAddr) -> Result<Self> {
+ let socket = Socket::new(AddressFamily::Inet, SockType::Stream, IpProtocol::Tcp)?;
+ socket.bind(address)?;
+ socket.listen(128)?;
+ Ok(Self(socket))
+ }
+
+ /// Returns the local address that this listener is bound to.
+ ///
+ /// See [`Socket::sockname()`] for more.
+ pub fn sockname(&self) -> Result<SocketAddr> {
+ self.0.sockname()
+ }
+
+ /// Returns an iterator over incoming connections.
+ ///
+ /// Each iteration will return a [`Result`] containing a [`TcpStream`] on success.
+ /// See [`TcpIncoming`] for more.
+ ///
+ /// # Examples
+ /// ```rust
+ /// use kernel::net::tcp::TcpListener;
+ /// use kernel::net::addr::*;
+ ///
+ /// let listener = TcpListener::new(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000))).unwrap();
+ /// for stream in listener.incoming() {
+ /// // ...
+ /// }
+ /// ```
+ pub fn incoming(&self) -> TcpIncoming<'_> {
+ TcpIncoming { listener: self }
+ }
+
+ /// Accepts an incoming connection.
+ ///
+ /// Returns a [`TcpStream`] on success.
+ pub fn accept(&self) -> Result<TcpStream> {
+ Ok(TcpStream(self.0.accept(true)?))
+ }
+
+ /// Sets the value of the given option.
+ ///
+ /// See [`Socket::set_option()`](Socket::set_option) for more.
+ pub fn set_option<O>(&self, value: impl Into<O::Type>) -> Result
+ where
+ O: SocketOption + WritableOption,
+ {
+ self.0.set_option::<O>(value)
+ }
+}
+
+/// An iterator over incoming connections from a [`TcpListener`].
+///
+/// Each iteration will return a [`Result`] containing a [`TcpStream`] on success.
+/// The iterator will never return [`None`].
+///
+/// This struct is created by the [`TcpListener::incoming()`] method.
+pub struct TcpIncoming<'a> {
+ listener: &'a TcpListener,
+}
+
+impl Iterator for TcpIncoming<'_> {
+ /// The item type of the iterator.
+ type Item = Result<TcpStream>;
+
+ /// Get the next connection from the listener.
+ fn next(&mut self) -> Option<Self::Item> {
+ Some(self.listener.accept())
+ }
+}
+
+/// A TCP stream.
+///
+/// Represents an active TCP connection between two sockets.
+/// The stream can be opened by the listener, with [`TcpListener::accept()`], or by
+/// connecting to a remote address with [`TcpStream::connect()`].
+/// The stream can be used to send and receive data.
+///
+/// See [`TcpListener`] for an example of how to create a [`TcpStream`].
+pub struct TcpStream(pub(crate) Socket);
+
+impl TcpStream {
+ /// Opens a TCP stream by connecting to the given address.
+ ///
+ /// Returns a [`TcpStream`] on success.
+ ///
+ /// # Examples
+ /// ```rust
+ /// use kernel::net::tcp::TcpStream;
+ /// use kernel::net::addr::*;
+ ///
+ /// let peer_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000));
+ /// let stream = TcpStream::connect(&peer_addr).unwrap();
+ /// ```
+ pub fn connect(address: &SocketAddr) -> Result<Self> {
+ let socket = Socket::new(AddressFamily::Inet, SockType::Stream, IpProtocol::Tcp)?;
+ socket.connect(address, 0)?;
+ Ok(Self(socket))
+ }
+
+ /// Returns the address of the remote peer of this connection.
+ ///
+ /// See [`Socket::peername()`] for more.
+ pub fn peername(&self) -> Result<SocketAddr> {
+ self.0.peername()
+ }
+
+ /// Returns the address of the local socket of this connection.
+ ///
+ /// See [`Socket::sockname()`] for more.
+ pub fn sockname(&self) -> Result<SocketAddr> {
+ self.0.sockname()
+ }
+
+ /// Receive data from the stream.
+ /// The given flags are used to modify the behavior of the receive operation.
+ /// See [`ReceiveFlag`] for more.
+ ///
+ /// Returns the number of bytes received.
+ ///
+ /// # Examples
+ /// ```rust
+ /// use kernel::flag_set;
+ /// use kernel::net::tcp::TcpListener;
+ /// use kernel::net::addr::*;
+ ///
+ /// let listener = TcpListener::new(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000))).unwrap();
+ /// while let Ok(stream) = listener.accept() {
+ /// let mut buf = [0u8; 1024];
+ /// while let Ok(len) = stream.receive(&mut buf, flag_set!()) {
+ /// // ...
+ /// }
+ /// }
+ /// ```
+ pub fn receive(&self, buf: &mut [u8], flags: FlagSet<ReceiveFlag>) -> Result<usize> {
+ self.0.receive(buf, flags)
+ }
+
+ /// Receive data from the stream and return the message header.
+ ///
+ /// The given flags are used to modify the behavior of the receive operation.
+ ///
+ /// Returns the number of bytes received and the message header, which contains
+ /// information about the sender and the message.
+ ///
+ /// See [`Socket::receive_msg()`] for more.
+ pub fn receive_msg(
+ &self,
+ buf: &mut [u8],
+ flags: FlagSet<ReceiveFlag>,
+ ) -> Result<(usize, MessageHeader)> {
+ self.0.receive_msg(buf, flags)
+ }
+
+ /// Send data to the stream.
+ ///
+ /// The given flags are used to modify the behavior of the send operation.
+ /// See [`SendFlag`] for more.
+ ///
+ /// Returns the number of bytes sent.
+ ///
+ /// # Examples
+ /// ```rust
+ /// use kernel::flag_set;
+ /// use kernel::net::tcp::TcpListener;
+ /// use kernel::net::addr::*;
+ ///
+ /// let listener = TcpListener::new(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000))).unwrap();
+ /// while let Ok(stream) = listener.accept() {
+ /// let mut buf = [0u8; 1024];
+ /// while let Ok(len) = stream.receive(&mut buf, flag_set!()) {
+ /// stream.send(&buf[..len], flag_set!())?;
+ /// }
+ /// }
+ /// ```
+ pub fn send(&self, buf: &[u8], flags: FlagSet<SendFlag>) -> Result<usize> {
+ self.0.send(buf, flags)
+ }
+
+ /// Manually shutdown some portion of the stream.
+ /// See [`ShutdownCmd`] for more.
+ ///
+ /// This method is not required to be called, as the stream will be shutdown
+ /// automatically when it is dropped.
+ ///
+ /// # Examples
+ /// ```rust
+ /// use kernel::net::tcp::TcpListener;
+ /// use kernel::net::addr::*;
+ /// use kernel::net::socket::ShutdownCmd;
+ ///
+ /// let listener = TcpListener::new(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::LOOPBACK, 8000))).unwrap();
+ /// while let Ok(stream) = listener.accept() {
+ /// // ...
+ /// stream.shutdown(ShutdownCmd::Both)?;
+ /// }
+ /// ```
+ pub fn shutdown(&self, how: ShutdownCmd) -> Result {
+ self.0.shutdown(how)
+ }
+}
+
+impl Drop for TcpStream {
+ /// Shutdown the stream.
+ ///
+ /// This method ignores the outcome of the shutdown operation: whether the stream
+ /// is successfully shutdown or not, the stream will be dropped anyways.
+ fn drop(&mut self) {
+ self.0.shutdown(ShutdownCmd::Both).ok();
+ }
+}
--
2.41.0
Create `net` module files and network headers in `bindings_helper.h`.
Add `IpProtocol`, `AddressFamily` and `Namespace`.
The wrappers added with this patch are shared across the whole network
subsystem. For this reason, they are placed in the `net.rs` module file.
The enum `IpProtocol`, however, is placed in an individual `ip.rs`
submodule, allowing to place together all the ip-related structures,
such as wrappers for `iphdr`, `ip_auth_hdr`, etc.
Signed-off-by: Michele Dalle Rive <[email protected]>
---
rust/bindings/bindings_helper.h | 3 +
rust/kernel/lib.rs | 2 +
rust/kernel/net.rs | 180 ++++++++++++++++++++++++++++++++
rust/kernel/net/ip.rs | 73 +++++++++++++
4 files changed, 258 insertions(+)
create mode 100644 rust/kernel/net.rs
create mode 100644 rust/kernel/net/ip.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 058954961bfc..7cc1bd73c77a 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -7,6 +7,9 @@
*/
#include <linux/errname.h>
+#include <linux/netfilter.h>
+#include <linux/inet.h>
+#include <linux/tcp.h>
#include <linux/slab.h>
#include <linux/refcount.h>
#include <linux/wait.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 85b261209977..8e6926d965e1 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -34,6 +34,8 @@
pub mod error;
pub mod init;
pub mod ioctl;
+#[cfg(CONFIG_NET)]
+pub mod net;
pub mod prelude;
pub mod print;
mod static_assert;
diff --git a/rust/kernel/net.rs b/rust/kernel/net.rs
new file mode 100644
index 000000000000..1eda336b48e4
--- /dev/null
+++ b/rust/kernel/net.rs
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Network subsystem.
+//!
+//! This module contains the kernel APIs related to networking that have been ported or wrapped for
+//! usage by Rust code in the kernel.
+//!
+//! C header: [`include/linux/net.h`](../../../../include/linux/net.h) and related
+
+use crate::error::{code, Error};
+use core::cell::UnsafeCell;
+pub mod ip;
+
+/// The address family.
+///
+/// See [`man 7 address families`](https://man7.org/linux/man-pages/man7/address_families.7.html) for more information.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum AddressFamily {
+ /// Unspecified address family.
+ Unspec = bindings::AF_UNSPEC as isize,
+ /// Local to host (pipes and file-domain).
+ Unix = bindings::AF_UNIX as isize,
+ /// Internetwork: UDP, TCP, etc.
+ Inet = bindings::AF_INET as isize,
+ /// Amateur radio AX.25.
+ Ax25 = bindings::AF_AX25 as isize,
+ /// IPX.
+ Ipx = bindings::AF_IPX as isize,
+ /// Appletalk DDP.
+ Appletalk = bindings::AF_APPLETALK as isize,
+ /// AX.25 packet layer protocol.
+ Netrom = bindings::AF_NETROM as isize,
+ /// Bridge link.
+ Bridge = bindings::AF_BRIDGE as isize,
+ /// ATM PVCs.
+ Atmpvc = bindings::AF_ATMPVC as isize,
+ /// X.25 (ISO-8208).
+ X25 = bindings::AF_X25 as isize,
+ /// IPv6.
+ Inet6 = bindings::AF_INET6 as isize,
+ /// ROSE protocol.
+ Rose = bindings::AF_ROSE as isize,
+ /// DECnet protocol.
+ Decnet = bindings::AF_DECnet as isize,
+ /// 802.2LLC project.
+ Netbeui = bindings::AF_NETBEUI as isize,
+ /// Firewall hooks.
+ Security = bindings::AF_SECURITY as isize,
+ /// Key management protocol.
+ Key = bindings::AF_KEY as isize,
+ /// Netlink.
+ Netlink = bindings::AF_NETLINK as isize,
+ /// Low-level packet interface.
+ Packet = bindings::AF_PACKET as isize,
+ /// Acorn Econet protocol.
+ Econet = bindings::AF_ECONET as isize,
+ /// ATM SVCs.
+ Atmsvc = bindings::AF_ATMSVC as isize,
+ /// RDS sockets.
+ Rds = bindings::AF_RDS as isize,
+ /// IRDA sockets.
+ Irda = bindings::AF_IRDA as isize,
+ /// Generic PPP.
+ Pppox = bindings::AF_PPPOX as isize,
+ /// Legacy WAN networks protocol.
+ Wanpipe = bindings::AF_WANPIPE as isize,
+ /// LLC protocol.
+ Llc = bindings::AF_LLC as isize,
+ /// Infiniband.
+ Ib = bindings::AF_IB as isize,
+ /// Multiprotocol label switching.
+ Mpls = bindings::AF_MPLS as isize,
+ /// Controller Area Network.
+ Can = bindings::AF_CAN as isize,
+ /// TIPC sockets.
+ Tipc = bindings::AF_TIPC as isize,
+ /// Bluetooth sockets.
+ Bluetooth = bindings::AF_BLUETOOTH as isize,
+ /// IUCV sockets.
+ Iucv = bindings::AF_IUCV as isize,
+ /// RxRPC sockets.
+ Rxrpc = bindings::AF_RXRPC as isize,
+ /// Modular ISDN protocol.
+ Isdn = bindings::AF_ISDN as isize,
+ /// Nokia cellular modem interface.
+ Phonet = bindings::AF_PHONET as isize,
+ /// IEEE 802.15.4 sockets.
+ Ieee802154 = bindings::AF_IEEE802154 as isize,
+ /// CAIF sockets.
+ Caif = bindings::AF_CAIF as isize,
+ /// Kernel crypto API
+ Alg = bindings::AF_ALG as isize,
+ /// VMware VSockets.
+ Vsock = bindings::AF_VSOCK as isize,
+ /// KCM sockets.
+ Kcm = bindings::AF_KCM as isize,
+ /// Qualcomm IPC router protocol.
+ Qipcrtr = bindings::AF_QIPCRTR as isize,
+ /// SMC sockets.
+ Smc = bindings::AF_SMC as isize,
+ /// Express Data Path sockets.
+ Xdp = bindings::AF_XDP as isize,
+}
+
+impl From<AddressFamily> for isize {
+ fn from(family: AddressFamily) -> Self {
+ family as isize
+ }
+}
+
+impl TryFrom<isize> for AddressFamily {
+ type Error = Error;
+
+ fn try_from(value: isize) -> Result<Self, Self::Error> {
+ let val = value as u32;
+ match val {
+ bindings::AF_UNSPEC => Ok(Self::Unspec),
+ bindings::AF_UNIX => Ok(Self::Unix),
+ bindings::AF_INET => Ok(Self::Inet),
+ bindings::AF_AX25 => Ok(Self::Ax25),
+ bindings::AF_IPX => Ok(Self::Ipx),
+ bindings::AF_APPLETALK => Ok(Self::Appletalk),
+ bindings::AF_NETROM => Ok(Self::Netrom),
+ bindings::AF_BRIDGE => Ok(Self::Bridge),
+ bindings::AF_ATMPVC => Ok(Self::Atmpvc),
+ bindings::AF_X25 => Ok(Self::X25),
+ bindings::AF_INET6 => Ok(Self::Inet6),
+ bindings::AF_ROSE => Ok(Self::Rose),
+ bindings::AF_DECnet => Ok(Self::Decnet),
+ bindings::AF_NETBEUI => Ok(Self::Netbeui),
+ bindings::AF_SECURITY => Ok(Self::Security),
+ bindings::AF_KEY => Ok(Self::Key),
+ bindings::AF_NETLINK => Ok(Self::Netlink),
+ bindings::AF_PACKET => Ok(Self::Packet),
+ bindings::AF_ECONET => Ok(Self::Econet),
+ bindings::AF_ATMSVC => Ok(Self::Atmsvc),
+ bindings::AF_RDS => Ok(Self::Rds),
+ bindings::AF_IRDA => Ok(Self::Irda),
+ bindings::AF_PPPOX => Ok(Self::Pppox),
+ bindings::AF_WANPIPE => Ok(Self::Wanpipe),
+ bindings::AF_LLC => Ok(Self::Llc),
+ bindings::AF_IB => Ok(Self::Ib),
+ bindings::AF_MPLS => Ok(Self::Mpls),
+ bindings::AF_CAN => Ok(Self::Can),
+ bindings::AF_TIPC => Ok(Self::Tipc),
+ bindings::AF_BLUETOOTH => Ok(Self::Bluetooth),
+ bindings::AF_IUCV => Ok(Self::Iucv),
+ bindings::AF_RXRPC => Ok(Self::Rxrpc),
+ bindings::AF_ISDN => Ok(Self::Isdn),
+ bindings::AF_PHONET => Ok(Self::Phonet),
+ bindings::AF_IEEE802154 => Ok(Self::Ieee802154),
+ bindings::AF_CAIF => Ok(Self::Caif),
+ bindings::AF_ALG => Ok(Self::Alg),
+ bindings::AF_VSOCK => Ok(Self::Vsock),
+ bindings::AF_KCM => Ok(Self::Kcm),
+ bindings::AF_QIPCRTR => Ok(Self::Qipcrtr),
+ bindings::AF_SMC => Ok(Self::Smc),
+ bindings::AF_XDP => Ok(Self::Xdp),
+ _ => Err(code::EINVAL),
+ }
+ }
+}
+
+/// Network namespace.
+///
+/// Wraps the `net` struct.
+#[repr(transparent)]
+pub struct Namespace(UnsafeCell<bindings::net>);
+
+/// The global network namespace.
+///
+/// This is the default and initial namespace.
+/// This function replaces the C `init_net` global variable.
+pub fn init_net() -> &'static Namespace {
+ // SAFETY: `init_net` is a global variable and is always valid.
+ let ptr = unsafe { core::ptr::addr_of!(bindings::init_net) };
+ // SAFETY: the address of `init_net` is always valid, always points to initialized memory,
+ // and is always aligned.
+ unsafe { &*(ptr.cast()) }
+}
diff --git a/rust/kernel/net/ip.rs b/rust/kernel/net/ip.rs
new file mode 100644
index 000000000000..84f98d356137
--- /dev/null
+++ b/rust/kernel/net/ip.rs
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! IP protocol definitions.
+//!
+//! This module contains the kernel structures and functions related to IP protocols.
+//!
+//! C headers:
+//! - [`include/linux/in.h`](../../../../include/linux/in.h)
+//! - [`include/linux/ip.h`](../../../../include/linux/ip.h)
+//! - [`include/uapi/linux/ip.h`](../../../../include/uapi/linux/ip.h)
+
+/// The Ip protocol.
+///
+/// See [`tools/include/uapi/linux/in.h`](../../../../tools/include/uapi/linux/in.h)
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum IpProtocol {
+ /// Dummy protocol for TCP
+ Ip = bindings::IPPROTO_IP as isize,
+ /// Internet Control Message Protocol
+ Icmp = bindings::IPPROTO_ICMP as isize,
+ /// Internet Group Management Protocol
+ Igmp = bindings::IPPROTO_IGMP as isize,
+ /// IPIP tunnels (older KA9Q tunnels use 94)
+ IpIp = bindings::IPPROTO_IPIP as isize,
+ /// Transmission Control Protocol
+ Tcp = bindings::IPPROTO_TCP as isize,
+ /// Exterior Gateway Protocol
+ Egp = bindings::IPPROTO_EGP as isize,
+ /// PUP protocol
+ Pup = bindings::IPPROTO_PUP as isize,
+ /// User Datagram Protocol
+ Udp = bindings::IPPROTO_UDP as isize,
+ /// XNS Idp protocol
+ Idp = bindings::IPPROTO_IDP as isize,
+ /// SO Transport Protocol Class 4
+ Tp = bindings::IPPROTO_TP as isize,
+ /// Datagram Congestion Control Protocol
+ Dccp = bindings::IPPROTO_DCCP as isize,
+ /// Ipv6-in-Ipv4 tunnelling
+ Ipv6 = bindings::IPPROTO_IPV6 as isize,
+ /// Rsvp Protocol
+ Rsvp = bindings::IPPROTO_RSVP as isize,
+ /// Cisco GRE tunnels (rfc 1701,1702)
+ Gre = bindings::IPPROTO_GRE as isize,
+ /// Encapsulation Security Payload protocol
+ Esp = bindings::IPPROTO_ESP as isize,
+ /// Authentication Header protocol
+ Ah = bindings::IPPROTO_AH as isize,
+ /// Multicast Transport Protocol
+ Mtp = bindings::IPPROTO_MTP as isize,
+ /// Ip option pseudo header for BEET
+ Beetph = bindings::IPPROTO_BEETPH as isize,
+ /// Encapsulation Header
+ Encap = bindings::IPPROTO_ENCAP as isize,
+ /// Protocol Independent Multicast
+ Pim = bindings::IPPROTO_PIM as isize,
+ /// Compression Header Protocol
+ Comp = bindings::IPPROTO_COMP as isize,
+ /// Layer 2 Tunnelling Protocol
+ L2Tp = bindings::IPPROTO_L2TP as isize,
+ /// Stream Control Transport Protocol
+ Sctp = bindings::IPPROTO_SCTP as isize,
+ /// Udp-Lite (Rfc 3828)
+ UdpLite = bindings::IPPROTO_UDPLITE as isize,
+ /// Mpls in Ip (Rfc 4023)
+ Mpls = bindings::IPPROTO_MPLS as isize,
+ /// Ethernet-within-Ipv6 Encapsulation
+ Ethernet = bindings::IPPROTO_ETHERNET as isize,
+ /// Raw Ip packets
+ Raw = bindings::IPPROTO_RAW as isize,
+ /// Multipath Tcp connection
+ Mptcp = bindings::IPPROTO_MPTCP as isize,
+}
--
2.41.0
Add enums representing flags related to sockets:
- `ReceiveFlag` to modify the behaviour of the socket receive operation.
- `SendFlag` to modify the behaviour of the socket send operation.
- `MessageFlag` to represent the flags in a `msghdr`.
- `SocketFlag` to represent the flags in the `socket` struct.
Introduce a `FlagSet` structure to offer a convenient way to handle the
flags.
Having an abstraction over the "raw" numerical value of the flags offers
many advantages:
- A `FlagSet` can be created in different ways: from an `IntoIterator`,
a value, a single flag or using the defined macro `flag_set!(...)`.
- Custom operations can be defined, such as the bitwise or.
- Flags in the set can be set, tested, unset through functions instead
of using bitwise operations.
- FlagSet implements the IntoIterator trait, allowing for iteration over
the flags contained.
Signed-off-by: Michele Dalle Rive <[email protected]>
---
Opinions on FlagSet? I think it might be a convenient structure, for any
kind of "bitmask flag", not necessarily limited to the socket ones,
since it provides methods to easily create and access the flags.
However, it might be cumbersome to use to just obtain the same result of
a simple bitwise OR.
rust/kernel/net/socket/flags.rs | 467 ++++++++++++++++++++++++++++++++
1 file changed, 467 insertions(+)
create mode 100644 rust/kernel/net/socket/flags.rs
diff --git a/rust/kernel/net/socket/flags.rs b/rust/kernel/net/socket/flags.rs
new file mode 100644
index 000000000000..fe98e09a8d46
--- /dev/null
+++ b/rust/kernel/net/socket/flags.rs
@@ -0,0 +1,467 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Socket-related flags and utilities.
+use crate::bindings;
+use core::fmt::Debug;
+use core::ops::{BitOr, BitOrAssign};
+
+/// Generic socket flag trait.
+///
+/// This trait represents any kind of flag with "bitmask" values (i.e. 0x1, 0x2, 0x4, 0x8, etc.)
+pub trait Flag:
+ Into<isize> + TryFrom<isize> + Debug + Copy + Clone + Send + Sync + 'static
+{
+}
+
+/// Socket send operation flags.
+///
+/// See <https://linux.die.net/man/2/sendmsg> for more.
+#[derive(Debug, Copy, Clone)]
+pub enum SendFlag {
+ /// Got a successful reply.
+ ///
+ /// Only valid for datagram and raw sockets.
+ /// Only valid for IPv4 and IPv6.
+ Confirm = bindings::MSG_CONFIRM as isize,
+
+ /// Don't use a gateway to send out the packet.
+ DontRoute = bindings::MSG_DONTROUTE as isize,
+
+ /// Enables nonblocking operation.
+ ///
+ /// If the operation would block, return immediately with an error.
+ DontWait = bindings::MSG_DONTWAIT as isize,
+
+ /// Terminates a record.
+ EOR = bindings::MSG_EOR as isize,
+
+ /// More data will be sent.
+ ///
+ /// Only valid for TCP and UDP sockets.
+ More = bindings::MSG_MORE as isize,
+
+ /// Don't send SIGPIPE error if the socket is shut down.
+ NoSignal = bindings::MSG_NOSIGNAL as isize,
+
+ /// Send out-of-band data on supported sockets.
+ OOB = bindings::MSG_OOB as isize,
+}
+
+impl From<SendFlag> for isize {
+ fn from(value: SendFlag) -> Self {
+ value as isize
+ }
+}
+
+impl TryFrom<isize> for SendFlag {
+ type Error = ();
+
+ fn try_from(value: isize) -> Result<SendFlag, Self::Error> {
+ let val = value as u32;
+ match val {
+ bindings::MSG_CONFIRM => Ok(SendFlag::Confirm),
+ bindings::MSG_DONTROUTE => Ok(SendFlag::DontRoute),
+ bindings::MSG_DONTWAIT => Ok(SendFlag::DontWait),
+ bindings::MSG_EOR => Ok(SendFlag::EOR),
+ bindings::MSG_MORE => Ok(SendFlag::More),
+ bindings::MSG_NOSIGNAL => Ok(SendFlag::NoSignal),
+ bindings::MSG_OOB => Ok(SendFlag::OOB),
+ _ => Err(()),
+ }
+ }
+}
+
+impl Flag for SendFlag {}
+
+/// Socket receive operation flags.
+///
+/// See <https://linux.die.net/man/2/recvmsg> for more.
+#[derive(Debug, Copy, Clone)]
+pub enum ReceiveFlag {
+ /// Enables nonblocking operation.
+ ///
+ /// If the operation would block, return immediately with an error.
+ DontWait = bindings::MSG_DONTWAIT as isize,
+
+ /// Specifies that queued errors should be received from the socket error queue.
+ ErrQueue = bindings::MSG_ERRQUEUE as isize,
+
+ /// Enables out-of-band reception.
+ OOB = bindings::MSG_OOB as isize,
+
+ /// Peeks at an incoming message.
+ ///
+ /// The data is treated as unread and the next recv() or similar function shall still return this data.
+ Peek = bindings::MSG_PEEK as isize,
+
+ /// Returns the real length of the packet, even when it was longer than the passed buffer.
+ ///
+ /// Only valid for raw, datagram, netlink and UNIX datagram sockets.
+ Trunc = bindings::MSG_TRUNC as isize,
+
+ /// Waits for the full request to be satisfied.
+ WaitAll = bindings::MSG_WAITALL as isize,
+}
+
+impl From<ReceiveFlag> for isize {
+ fn from(value: ReceiveFlag) -> Self {
+ value as isize
+ }
+}
+
+impl TryFrom<isize> for ReceiveFlag {
+ type Error = ();
+
+ fn try_from(value: isize) -> Result<Self, Self::Error> {
+ let val = value as u32;
+ match val {
+ bindings::MSG_DONTWAIT => Ok(ReceiveFlag::DontWait),
+ bindings::MSG_ERRQUEUE => Ok(ReceiveFlag::ErrQueue),
+ bindings::MSG_OOB => Ok(ReceiveFlag::OOB),
+ bindings::MSG_PEEK => Ok(ReceiveFlag::Peek),
+ bindings::MSG_TRUNC => Ok(ReceiveFlag::Trunc),
+ bindings::MSG_WAITALL => Ok(ReceiveFlag::WaitAll),
+ _ => Err(()),
+ }
+ }
+}
+
+impl Flag for ReceiveFlag {}
+
+/// Socket `flags` field flags.
+///
+/// These flags are used internally by the kernel.
+/// However, they are exposed here for completeness.
+///
+/// This enum does not implement the `Flag` trait, since it is not actually a flag.
+/// Flags are often defined as a mask that can be used to retrieve the flag value; the socket flags,
+/// instead, are defined as the index of the bit that they occupy in the `flags` field.
+/// This means that they cannot be used as a mask, just like all the other flags that implement `Flag` do.
+///
+/// For example, SOCK_PASSCRED has value 3, meaning that it is represented by the 3rd bit of the `flags` field;
+/// a normal flag would represent it as a mask, i.e. 1 << 3 = 0b1000.
+///
+/// See [include/linux/net.h](../../../../include/linux/net.h) for more.
+pub enum SocketFlag {
+ /// Undocumented.
+ NoSpace = bindings::SOCK_NOSPACE as isize,
+ /// Undocumented.
+ PassCred = bindings::SOCK_PASSCRED as isize,
+ /// Undocumented.
+ PassSecurity = bindings::SOCK_PASSSEC as isize,
+ /// Undocumented.
+ SupportZeroCopy = bindings::SOCK_SUPPORT_ZC as isize,
+ /// Undocumented.
+ CustomSockOpt = bindings::SOCK_CUSTOM_SOCKOPT as isize,
+ /// Undocumented.
+ PassPidFd = bindings::SOCK_PASSPIDFD as isize,
+}
+
+impl From<SocketFlag> for isize {
+ fn from(value: SocketFlag) -> Self {
+ value as isize
+ }
+}
+
+impl TryFrom<isize> for SocketFlag {
+ type Error = ();
+
+ fn try_from(value: isize) -> Result<Self, Self::Error> {
+ let val = value as u32;
+ match val {
+ bindings::SOCK_NOSPACE => Ok(SocketFlag::NoSpace),
+ bindings::SOCK_PASSCRED => Ok(SocketFlag::PassCred),
+ bindings::SOCK_PASSSEC => Ok(SocketFlag::PassSecurity),
+ bindings::SOCK_SUPPORT_ZC => Ok(SocketFlag::SupportZeroCopy),
+ bindings::SOCK_CUSTOM_SOCKOPT => Ok(SocketFlag::CustomSockOpt),
+ bindings::SOCK_PASSPIDFD => Ok(SocketFlag::PassPidFd),
+ _ => Err(()),
+ }
+ }
+}
+
+/// Flags associated with a received message.
+///
+/// Represents the flag contained in the `msg_flags` field of a `msghdr` struct.
+#[derive(Debug, Copy, Clone)]
+pub enum MessageFlag {
+ /// End of record.
+ Eor = bindings::MSG_EOR as isize,
+ /// Trailing portion of the message is discarded.
+ Trunc = bindings::MSG_TRUNC as isize,
+ /// Control data was discarded due to lack of space.
+ Ctrunc = bindings::MSG_CTRUNC as isize,
+ /// Out-of-band data was received.
+ Oob = bindings::MSG_OOB as isize,
+ /// An error was received instead of data.
+ ErrQueue = bindings::MSG_ERRQUEUE as isize,
+}
+
+impl From<MessageFlag> for isize {
+ fn from(value: MessageFlag) -> Self {
+ value as isize
+ }
+}
+
+impl TryFrom<isize> for MessageFlag {
+ type Error = ();
+
+ fn try_from(value: isize) -> Result<Self, Self::Error> {
+ let val = value as u32;
+ match val {
+ bindings::MSG_EOR => Ok(MessageFlag::Eor),
+ bindings::MSG_TRUNC => Ok(MessageFlag::Trunc),
+ bindings::MSG_CTRUNC => Ok(MessageFlag::Ctrunc),
+ bindings::MSG_OOB => Ok(MessageFlag::Oob),
+ bindings::MSG_ERRQUEUE => Ok(MessageFlag::ErrQueue),
+ _ => Err(()),
+ }
+ }
+}
+
+impl Flag for MessageFlag {}
+
+/// Structure representing a set of flags.
+///
+/// This structure is used to represent a set of flags, such as the flags passed to `send` or `recv`.
+/// It is generic over the type of flag that it contains.
+///
+/// # Invariants
+/// The value of the flags must be a valid combination of the flags that it contains.
+///
+/// This means that the value must be the bitwise OR of the values of the flags, and that it
+/// must be possible to retrieve the value of the flags from the value.
+///
+/// # Example
+/// ```
+/// use kernel::net::socket::flags::{SendFlag, FlagSet};
+///
+/// let mut flags = FlagSet::<SendFlag>::empty();
+/// flags.insert(SendFlag::DontWait);
+/// flags.insert(SendFlag::More);
+/// assert!(flags.contains(SendFlag::DontWait));
+/// assert!(flags.contains(SendFlag::More));
+/// flags.clear();
+/// assert_eq!(flags.value(), 0);
+///
+/// flags = FlagSet::<SendFlag>::from(SendFlag::More);
+/// flags |= SendFlag::DontWait;
+/// assert!(flags.contains(SendFlag::DontWait));
+/// ```
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FlagSet<T: Flag> {
+ value: isize,
+ _phantom: core::marker::PhantomData<T>,
+}
+
+impl<T: Flag> FlagSet<T> {
+ /// Create a new empty set of flags.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let flags = FlagSet::<SendFlag>::empty();
+ /// assert_eq!(flags.value(), 0);
+ /// ```
+ pub fn empty() -> Self {
+ FlagSet {
+ value: 0,
+ _phantom: core::marker::PhantomData,
+ }
+ }
+
+ /// Clear all the flags set.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let mut flags = FlagSet::<SendFlag>::from(SendFlag::More);
+ /// assert!(flags.contains(SendFlag::More));
+ /// flags.clear();
+ /// assert_eq!(flags.value(), 0);
+ /// ```
+ pub fn clear(&mut self) {
+ self.value = 0;
+ }
+
+ /// Add a flag to the set.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let mut flags = FlagSet::<SendFlag>::empty();
+ /// assert!(!flags.contains(SendFlag::DontWait));
+ /// flags.insert(SendFlag::DontWait);
+ /// assert!(flags.contains(SendFlag::DontWait));
+ /// ```
+ pub fn insert(&mut self, flag: T) {
+ self.value |= flag.into();
+ }
+
+ /// Remove a flag from the set.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let mut flags = FlagSet::<SendFlag>::from(SendFlag::DontWait);
+ /// assert!(flags.contains(SendFlag::DontWait));
+ /// flags.remove(SendFlag::DontWait);
+ /// assert!(!flags.contains(SendFlag::DontWait));
+ /// ```
+ pub fn remove(&mut self, flag: T) {
+ self.value &= !flag.into();
+ }
+
+ /// Check if a flag is set.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let mut flags = FlagSet::<SendFlag>::from(SendFlag::DontWait);
+ /// assert!(flags.contains(SendFlag::DontWait));
+ /// ```
+ pub fn contains(&self, flag: T) -> bool {
+ self.value & flag.into() != 0
+ }
+
+ /// Get the integer value of the flags set.
+ ///
+ /// # Example
+ /// ```
+ /// use kernel::net::socket::flags::{SendFlag, FlagSet};
+ ///
+ /// let flags = FlagSet::<SendFlag>::from(SendFlag::DontWait);
+ /// assert_eq!(flags.value(), SendFlag::DontWait as isize);
+ /// ```
+ pub fn value(&self) -> isize {
+ self.value
+ }
+}
+
+impl<T: Flag> BitOr<T> for FlagSet<T> {
+ type Output = FlagSet<T>;
+
+ fn bitor(self, rhs: T) -> Self::Output {
+ FlagSet {
+ value: self.value | rhs.into(),
+ _phantom: core::marker::PhantomData,
+ }
+ }
+}
+
+impl<T: Flag> BitOrAssign<T> for FlagSet<T> {
+ fn bitor_assign(&mut self, rhs: T) {
+ self.value |= rhs.into();
+ }
+}
+
+// impl from isize for any flags<T>
+impl<T: Flag> From<isize> for FlagSet<T> {
+ fn from(value: isize) -> Self {
+ FlagSet {
+ value,
+ _phantom: core::marker::PhantomData,
+ }
+ }
+}
+
+impl<T: Flag> From<T> for FlagSet<T> {
+ fn from(value: T) -> Self {
+ Self::from(value.into())
+ }
+}
+
+impl<T: Flag> FromIterator<T> for FlagSet<T> {
+ fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
+ let mut flags = FlagSet::empty();
+ for flag in iter {
+ flags.insert(flag);
+ }
+ flags
+ }
+}
+
+impl<T: Flag> From<FlagSet<T>> for isize {
+ fn from(value: FlagSet<T>) -> Self {
+ value.value
+ }
+}
+
+impl<T: Flag> IntoIterator for FlagSet<T> {
+ type Item = T;
+ type IntoIter = FlagSetIterator<T>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ FlagSetIterator {
+ flags: self,
+ current: 0,
+ }
+ }
+}
+
+/// Iterator over the flags in a set.
+///
+/// This iterator iterates over the flags in a set, in order of increasing value.
+///
+/// # Example
+/// ```
+/// use kernel::net::socket::flags::{SendFlag, FlagSet};
+///
+/// let mut flags = FlagSet::from_iter([SendFlag::DontWait, SendFlag::More]);
+/// for flag in flags.into_iter() {
+/// println!("Flag: {:?}", flag);
+/// }
+/// ```
+pub struct FlagSetIterator<T: Flag> {
+ flags: FlagSet<T>,
+ current: usize,
+}
+
+impl<T: Flag> Iterator for FlagSetIterator<T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let mut value = 1 << self.current;
+ while value <= self.flags.value {
+ self.current += 1;
+ if self.flags.value & value != 0 {
+ if let Ok(flag) = T::try_from(value) {
+ return Some(flag);
+ }
+ }
+ value = 1 << self.current;
+ }
+ None
+ }
+}
+
+/// Create a set of flags from a list of flags.
+///
+/// This macro provides a compact way to create empty sets and sets from a list of flags.
+///
+/// # Example
+/// ```
+/// use kernel::net::socket::flags::SendFlag;
+/// use kernel::flag_set;
+///
+/// let mut flags = flag_set!(SendFlag::DontWait, SendFlag::More);
+/// assert!(flags.contains(SendFlag::DontWait));
+/// assert!(flags.contains(SendFlag::More));
+///
+/// let mut empty_flags = flag_set!();
+/// assert_eq!(empty_flags.value(), 0);
+/// ```
+#[macro_export]
+macro_rules! flag_set {
+ () => {
+ $crate::net::socket::flags::FlagSet::empty()
+ };
+ ($($flag:expr),+) => {
+ $crate::net::socket::flags::FlagSet::from_iter([$($flag),+])
+ };
+}
--
2.41.0
On Mon, Aug 14, 2023 at 11:22:55AM +0200, Michele Dalle Rive wrote:
> This patch series is intended to create Rust abstractions for Sockets
> and other fundamental network entities.
>
> Specifically, it was added:
> - Ip address and Socket address wrappers (for `in_addr`, `in6_addr`,
> `sockaddr_in`, `sockaddr_in6`, `sockaddr_storage`).
> - Socket wrapper.
> - Socket flags and options enums.
> - TCP and UDP specific abstractions over the Rust Socket structure.
>
> This series is a RFC because I would appreciate some feedback about:
> - The structure of the module: is the division of the files and modules
> appropriate or should it be more or less fine-grained?
> Also, should the `net` module export all the structures of its
> submodules? I noticed that it is done in the standard library.
> - Whether the documentation is comprehensive enough.
> - A few other specific questions, written in the individual patches.
>
> I would greatly appreciate any kind of feedback or opinion.
> I am pretty new to the patch/mailing list world, so please point out any
> mistake I might make.
The best feedback is "who will use these new interfaces?" Without that,
it's really hard to review a patchset as it's difficult to see how the
bindings will be used, right?
thanks,
greg k-h
On Thu, 17 Aug 2023 16:53:03 +0200 Michele Dalle Rive wrote:
> in the last few days, I had the opportunity to discuss with some people from
> the RustForLinux community.
>
> I apologize for not being clear: the goal of these APIs was to give some
> network support to, in particular, out-of-tree modules; they were not meant to
> be used by a specific module that was planned to get upstreamed as well.
> The idea behind this patch is that, as of now, Rust is not a viable option for
> any OOT module that requires even the highest-level network support.
>
> I am wondering whether the `net` subsystem is interested in reviewing, giving
> feedback and eventually accepting code that is currently OOT-only.
This is a bit concerning. You can white out Rust in that and plonk in
some corporate backed project people tried to cram into the kernel
without understanding the community aspects. I'm not saying it's
the same but the tone reads the same.
"The `net` subsystem" have given "the RustForLinux community" clear
guidance on what a good integration starting point is. And now someone
else from Rust comes in and talk about supporting OOT modules.
I thought the Rust was just shaking up the languages we use, not the
fundamentals on how this project operates :|
Jakub Kicinski <[email protected]> writes:
> I thought the Rust was just shaking up the languages we use, not the
> fundamentals on how this project operates :|
I don't think this little episode has anything to do with Rust, really;
certainly we've had no shortage of companies wanting support for
out-of-tree modules written in C over the years.
Instead, this is just the sort of thing you see when people who are new
to our community learn - the hard way, sometimes - how kernel
development works. Rust seems likely to bring in a fair number of new
developers, which is great, but we're going to have to help them learn
their way around.
Thanks,
jon