mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-09 13:26:48 +00:00
Add basic structure for netlink route socket
This commit is contained in:
parent
2c41055470
commit
ac42e83387
@ -5,6 +5,7 @@ pub mod socket;
|
|||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
iface::init();
|
iface::init();
|
||||||
|
socket::netlink::init();
|
||||||
socket::vsock::init();
|
socket::vsock::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub mod ip;
|
pub mod ip;
|
||||||
|
pub mod netlink;
|
||||||
pub mod options;
|
pub mod options;
|
||||||
pub mod unix;
|
pub mod unix;
|
||||||
mod util;
|
mod util;
|
||||||
|
71
kernel/src/net/socket/netlink/addr/mod.rs
Normal file
71
kernel/src/net/socket/netlink/addr/mod.rs
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
mod multicast;
|
||||||
|
|
||||||
|
pub use multicast::{GroupIdSet, MAX_GROUPS};
|
||||||
|
|
||||||
|
use crate::{net::socket::SocketAddr, prelude::*};
|
||||||
|
|
||||||
|
/// The socket address of a netlink socket.
|
||||||
|
///
|
||||||
|
/// The address contains the port number for unicast
|
||||||
|
/// and the group IDs for multicast.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct NetlinkSocketAddr {
|
||||||
|
port: PortNum,
|
||||||
|
groups: GroupIdSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetlinkSocketAddr {
|
||||||
|
/// Creates a new netlink address.
|
||||||
|
pub const fn new(port: PortNum, groups: GroupIdSet) -> Self {
|
||||||
|
Self { port, groups }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new unspecified address.
|
||||||
|
///
|
||||||
|
/// Both the port ID and group numbers are left unspecified.
|
||||||
|
///
|
||||||
|
/// Note that an unspecified address can also represent the kernel socket address.
|
||||||
|
pub const fn new_unspecified() -> Self {
|
||||||
|
Self {
|
||||||
|
port: UNSPECIFIED_PORT,
|
||||||
|
groups: GroupIdSet::new_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the port number.
|
||||||
|
pub const fn port(&self) -> PortNum {
|
||||||
|
self.port
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the group ID set.
|
||||||
|
pub const fn groups(&self) -> GroupIdSet {
|
||||||
|
self.groups
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SocketAddr> for NetlinkSocketAddr {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: SocketAddr) -> Result<Self> {
|
||||||
|
match value {
|
||||||
|
SocketAddr::Netlink(addr) => Ok(addr),
|
||||||
|
_ => return_errno_with_message!(
|
||||||
|
Errno::EAFNOSUPPORT,
|
||||||
|
"the address is in an unsupported address family"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NetlinkSocketAddr> for SocketAddr {
|
||||||
|
fn from(value: NetlinkSocketAddr) -> Self {
|
||||||
|
SocketAddr::Netlink(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type NetlinkProtocolId = u32;
|
||||||
|
pub type PortNum = u32;
|
||||||
|
|
||||||
|
pub const UNSPECIFIED_PORT: PortNum = 0;
|
91
kernel/src/net/socket/netlink/addr/multicast.rs
Normal file
91
kernel/src/net/socket/netlink/addr/multicast.rs
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
/// A set of group IDs.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct GroupIdSet(u32);
|
||||||
|
|
||||||
|
impl GroupIdSet {
|
||||||
|
/// Creates a new empty `GroupIdSet`.
|
||||||
|
pub const fn new_empty() -> Self {
|
||||||
|
Self(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new `GroupIdSet` with multiple groups.
|
||||||
|
///
|
||||||
|
/// Each 1 bit in `groups` represent a group.
|
||||||
|
pub const fn new(groups: u32) -> Self {
|
||||||
|
Self(groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an iterator over all group IDs.
|
||||||
|
pub const fn ids_iter(&self) -> GroupIdIter {
|
||||||
|
GroupIdIter::new(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new group.
|
||||||
|
///
|
||||||
|
/// If the group already exists, this method will return an error.
|
||||||
|
pub fn add_group(&mut self, group_id: GroupId) -> Result<()> {
|
||||||
|
if group_id >= 32 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the group ID is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mask = 1u32 << group_id;
|
||||||
|
if self.0 & mask != 0 {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the group ID already exists");
|
||||||
|
}
|
||||||
|
self.0 |= mask;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets new groups.
|
||||||
|
pub fn set_groups(&mut self, new_groups: u32) {
|
||||||
|
self.0 = new_groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears all groups.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.0 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the set of group IDs is empty.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the group IDs as a u32.
|
||||||
|
pub fn as_u32(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterator over a set of group IDs.
|
||||||
|
pub struct GroupIdIter {
|
||||||
|
groups: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GroupIdIter {
|
||||||
|
const fn new(groups: &GroupIdSet) -> Self {
|
||||||
|
Self { groups: groups.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for GroupIdIter {
|
||||||
|
type Item = GroupId;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.groups > 0 {
|
||||||
|
let group_id = self.groups.trailing_zeros();
|
||||||
|
self.groups &= self.groups - 1;
|
||||||
|
return Some(group_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAX_GROUPS: u32 = 32;
|
||||||
|
pub type GroupId = u32;
|
51
kernel/src/net/socket/netlink/mod.rs
Normal file
51
kernel/src/net/socket/netlink/mod.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! This module defines netlink sockets.
|
||||||
|
//!
|
||||||
|
//! Netlink provides a standardized, socket-based interface,
|
||||||
|
//! typically used for communication between user space and kernel space.
|
||||||
|
//! It can also be used for interaction between two user processes.
|
||||||
|
//!
|
||||||
|
//! Each netlink socket corresponds to
|
||||||
|
//! a netlink protocol identified by a protocol ID (u32).
|
||||||
|
//! Protocols are generally defined to serve specific functions.
|
||||||
|
//! For instance, the NETLINK_ROUTE protocol is employed
|
||||||
|
//! to retrieve or modify network device settings.
|
||||||
|
//! Only sockets associated with the same protocol can communicate with each other.
|
||||||
|
//! Some protocols are pre-defined by the kernel and serve fixed purposes,
|
||||||
|
//! but users can also establish custom protocols by specifying new protocol IDs.
|
||||||
|
//!
|
||||||
|
//! Before initiating communication,
|
||||||
|
//! a netlink socket must be bound to an address,
|
||||||
|
//! which consists of a port number and a multicast group number.
|
||||||
|
//!
|
||||||
|
//! The port number is used for unicast communication,
|
||||||
|
//! whereas the multicast group number is meant for multicast communication.
|
||||||
|
//!
|
||||||
|
//! In terms of unicast communication within each protocol,
|
||||||
|
//! a port number can only be bound to one socket.
|
||||||
|
//! However, the same port number can be utilized across different protocols.
|
||||||
|
//! Typically, the port number corresponds to the process ID of the running process.
|
||||||
|
//!
|
||||||
|
//! Multicast communication allows a message
|
||||||
|
//! to be sent to one or multiple multicast groups simultaneously.
|
||||||
|
//! Each protocol can support up to 32 multicast groups,
|
||||||
|
//! and a socket can belong to zero or multiple multicast groups.
|
||||||
|
//!
|
||||||
|
//! Netlink communication is akin to UDP in that
|
||||||
|
//! it does not require a connection to be established before sending messages.
|
||||||
|
//! The destination address must be specified when dispatching a message.
|
||||||
|
//!
|
||||||
|
|
||||||
|
mod addr;
|
||||||
|
mod message;
|
||||||
|
mod route;
|
||||||
|
mod table;
|
||||||
|
|
||||||
|
pub use addr::{GroupIdSet, NetlinkSocketAddr};
|
||||||
|
pub use route::NetlinkRouteSocket;
|
||||||
|
pub use table::{is_valid_protocol, StandardNetlinkProtocol};
|
||||||
|
|
||||||
|
pub(in crate::net) fn init() {
|
||||||
|
table::init();
|
||||||
|
}
|
139
kernel/src/net/socket/netlink/route/bound.rs
Normal file
139
kernel/src/net/socket/netlink/route/bound.rs
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use core::ops::Sub;
|
||||||
|
|
||||||
|
use super::message::RtnlMessage;
|
||||||
|
use crate::{
|
||||||
|
events::IoEvents,
|
||||||
|
net::socket::{
|
||||||
|
netlink::{
|
||||||
|
message::ProtocolSegment, route::kernel::get_netlink_route_kernel, table::BoundHandle,
|
||||||
|
NetlinkSocketAddr,
|
||||||
|
},
|
||||||
|
SendRecvFlags,
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
util::{MultiRead, MultiWrite},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) struct BoundNetlinkRoute {
|
||||||
|
handle: BoundHandle,
|
||||||
|
receive_queue: Mutex<VecDeque<RtnlMessage>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundNetlinkRoute {
|
||||||
|
pub(super) const fn new(handle: BoundHandle) -> Self {
|
||||||
|
Self {
|
||||||
|
handle,
|
||||||
|
receive_queue: Mutex::new(VecDeque::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) const fn addr(&self) -> NetlinkSocketAddr {
|
||||||
|
self.handle.addr()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn try_send(
|
||||||
|
&self,
|
||||||
|
reader: &mut dyn MultiRead,
|
||||||
|
remote: Option<&NetlinkSocketAddr>,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<usize> {
|
||||||
|
// TODO: Deal with flags
|
||||||
|
if !flags.is_all_supported() {
|
||||||
|
warn!("unsupported flags: {:?}", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(remote) = remote {
|
||||||
|
// TODO: Further check whether other socket address can be supported.
|
||||||
|
if *remote != NetlinkSocketAddr::new_unspecified() {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::ECONNREFUSED,
|
||||||
|
"sending netlink route messages to user space is not supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: We should use the connected remote address, if any.
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nlmsg = {
|
||||||
|
let sum_lens = reader.sum_lens();
|
||||||
|
|
||||||
|
match RtnlMessage::read_from(reader) {
|
||||||
|
Ok(nlmsg) => nlmsg,
|
||||||
|
Err(e) if e.error() == Errno::EFAULT => {
|
||||||
|
// EFAULT indicates an error occurred while copying data from user space,
|
||||||
|
// and this error should be returned back to user space.
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
// Errors other than EFAULT indicate a failure in parsing the netlink message.
|
||||||
|
// These errors should be silently ignored.
|
||||||
|
warn!("failed to send netlink message: {:?}", e);
|
||||||
|
return Ok(sum_lens);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let local_port = self.addr().port();
|
||||||
|
for segment in nlmsg.segments_mut() {
|
||||||
|
// The header's PID should be the sender's port ID.
|
||||||
|
// However, the sender can also leave it unspecified.
|
||||||
|
// In such cases, we will manually set the PID to the sender's port ID.
|
||||||
|
let header = segment.header_mut();
|
||||||
|
if header.pid == 0 {
|
||||||
|
header.pid = local_port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get_netlink_route_kernel().request(&nlmsg, |response| {
|
||||||
|
self.receive_queue.lock().push_back(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(nlmsg.total_len())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn try_receive(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn MultiWrite,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<(usize, NetlinkSocketAddr)> {
|
||||||
|
// TODO: Deal with other flags. Only MSG_PEEK is handled here.
|
||||||
|
if !flags.sub(SendRecvFlags::MSG_PEEK).is_all_supported() {
|
||||||
|
warn!("unsupported flags: {:?}", flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut receive_queue = self.receive_queue.lock();
|
||||||
|
|
||||||
|
let Some(response) = receive_queue.front() else {
|
||||||
|
return_errno_with_message!(Errno::EAGAIN, "nothing to receive");
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = {
|
||||||
|
let max_len = writer.sum_lens();
|
||||||
|
response.total_len().min(max_len)
|
||||||
|
};
|
||||||
|
|
||||||
|
response.write_to(writer)?;
|
||||||
|
|
||||||
|
if !flags.contains(SendRecvFlags::MSG_PEEK) {
|
||||||
|
receive_queue.pop_front().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: The message can only come from kernel socket currently.
|
||||||
|
let remote = NetlinkSocketAddr::new_unspecified();
|
||||||
|
|
||||||
|
Ok((len, remote))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check_io_events(&self) -> IoEvents {
|
||||||
|
let mut events = IoEvents::OUT;
|
||||||
|
|
||||||
|
let receive_queue = self.receive_queue.lock();
|
||||||
|
if !receive_queue.is_empty() {
|
||||||
|
events |= IoEvents::IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
events
|
||||||
|
}
|
||||||
|
}
|
207
kernel/src/net/socket/netlink/route/mod.rs
Normal file
207
kernel/src/net/socket/netlink/route/mod.rs
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
//! Netlink Route Socket.
|
||||||
|
|
||||||
|
use core::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
|
||||||
|
use bound::BoundNetlinkRoute;
|
||||||
|
use takeable::Takeable;
|
||||||
|
use unbound::UnboundNetlinkRoute;
|
||||||
|
|
||||||
|
use super::NetlinkSocketAddr;
|
||||||
|
use crate::{
|
||||||
|
events::IoEvents,
|
||||||
|
net::socket::{
|
||||||
|
options::SocketOption, private::SocketPrivate, MessageHeader, SendRecvFlags, Socket,
|
||||||
|
SocketAddr,
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
process::signal::{PollHandle, Pollable, Pollee},
|
||||||
|
util::{MultiRead, MultiWrite},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod bound;
|
||||||
|
mod kernel;
|
||||||
|
mod message;
|
||||||
|
mod unbound;
|
||||||
|
|
||||||
|
pub struct NetlinkRouteSocket {
|
||||||
|
is_nonblocking: AtomicBool,
|
||||||
|
pollee: Pollee,
|
||||||
|
inner: RwMutex<Takeable<Inner>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Inner {
|
||||||
|
Unbound(UnboundNetlinkRoute),
|
||||||
|
Bound(BoundNetlinkRoute),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetlinkRouteSocket {
|
||||||
|
pub fn new(is_nonblocking: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
is_nonblocking: AtomicBool::new(is_nonblocking),
|
||||||
|
pollee: Pollee::new(),
|
||||||
|
inner: RwMutex::new(Takeable::new(Inner::Unbound(UnboundNetlinkRoute::new()))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_receive(
|
||||||
|
&self,
|
||||||
|
writer: &mut dyn MultiWrite,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<(usize, NetlinkSocketAddr)> {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
|
||||||
|
let bound = match inner.as_ref() {
|
||||||
|
Inner::Unbound(_) => {
|
||||||
|
return_errno_with_message!(Errno::EAGAIN, "the socket is not bound")
|
||||||
|
}
|
||||||
|
Inner::Bound(bound_netlink_route) => bound_netlink_route,
|
||||||
|
};
|
||||||
|
|
||||||
|
let received = bound.try_receive(writer, flags)?;
|
||||||
|
self.pollee.invalidate();
|
||||||
|
|
||||||
|
Ok(received)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_send(
|
||||||
|
&self,
|
||||||
|
reader: &mut dyn MultiRead,
|
||||||
|
remote: Option<&NetlinkSocketAddr>,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
|
||||||
|
let bound = match inner.as_ref() {
|
||||||
|
Inner::Unbound(_) => todo!(),
|
||||||
|
Inner::Bound(bound) => bound,
|
||||||
|
};
|
||||||
|
|
||||||
|
let sent_bytes = bound.try_send(reader, remote, flags)?;
|
||||||
|
self.pollee.notify(IoEvents::OUT | IoEvents::IN);
|
||||||
|
|
||||||
|
Ok(sent_bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_io_events(&self) -> IoEvents {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
match inner.as_ref() {
|
||||||
|
Inner::Unbound(unbound) => unbound.check_io_events(),
|
||||||
|
Inner::Bound(bound) => bound.check_io_events(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Socket for NetlinkRouteSocket {
|
||||||
|
fn bind(&self, socket_addr: SocketAddr) -> Result<()> {
|
||||||
|
let SocketAddr::Netlink(netlink_addr) = socket_addr else {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EAFNOSUPPORT,
|
||||||
|
"the provided address is not netlink address"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut inner = self.inner.write();
|
||||||
|
inner.borrow_result(|owned_inner| match owned_inner.bind(&netlink_addr) {
|
||||||
|
Ok(bound_inner) => (bound_inner, Ok(())),
|
||||||
|
Err((err, err_inner)) => (err_inner, Err(err)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn addr(&self) -> Result<SocketAddr> {
|
||||||
|
let netlink_addr = match self.inner.read().as_ref() {
|
||||||
|
Inner::Unbound(_) => NetlinkSocketAddr::new_unspecified(),
|
||||||
|
Inner::Bound(bound) => bound.addr(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(SocketAddr::Netlink(netlink_addr))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sendmsg(
|
||||||
|
&self,
|
||||||
|
reader: &mut dyn MultiRead,
|
||||||
|
message_header: MessageHeader,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let MessageHeader {
|
||||||
|
addr,
|
||||||
|
control_message,
|
||||||
|
} = message_header;
|
||||||
|
|
||||||
|
let remote = match addr {
|
||||||
|
None => None,
|
||||||
|
Some(addr) => Some(addr.try_into()?),
|
||||||
|
};
|
||||||
|
|
||||||
|
if control_message.is_some() {
|
||||||
|
// TODO: Support sending control message
|
||||||
|
warn!("sending control message is not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make sure our blocking behavior matches that of Linux
|
||||||
|
self.try_send(reader, remote.as_ref(), flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn recvmsg(
|
||||||
|
&self,
|
||||||
|
writers: &mut dyn MultiWrite,
|
||||||
|
flags: SendRecvFlags,
|
||||||
|
) -> Result<(usize, MessageHeader)> {
|
||||||
|
let (received_len, addr) =
|
||||||
|
self.block_on(IoEvents::IN, || self.try_receive(writers, flags))?;
|
||||||
|
|
||||||
|
// TODO: Receive control message
|
||||||
|
|
||||||
|
let message_header = {
|
||||||
|
let addr = SocketAddr::Netlink(addr);
|
||||||
|
MessageHeader::new(Some(addr), None)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((received_len, message_header))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_option(&self, _option: &dyn SocketOption) -> Result<()> {
|
||||||
|
// TODO: This dummy option is added to pass the libnl test
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SocketPrivate for NetlinkRouteSocket {
|
||||||
|
fn is_nonblocking(&self) -> bool {
|
||||||
|
self.is_nonblocking.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_nonblocking(&self, nonblocking: bool) {
|
||||||
|
self.is_nonblocking.store(nonblocking, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pollable for NetlinkRouteSocket {
|
||||||
|
fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents {
|
||||||
|
self.pollee
|
||||||
|
.poll_with(mask, poller, || self.check_io_events())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Inner {
|
||||||
|
fn bind(self, addr: &NetlinkSocketAddr) -> core::result::Result<Self, (Error, Self)> {
|
||||||
|
let unbound = match self {
|
||||||
|
Inner::Unbound(unbound) => unbound,
|
||||||
|
Inner::Bound(bound) => {
|
||||||
|
// FIXME: We need to further check the Linux behavior
|
||||||
|
// whether we should return error if the socket is bound.
|
||||||
|
// The socket may call `bind` syscall to join new multicast groups.
|
||||||
|
return Err((
|
||||||
|
Error::with_message(Errno::EINVAL, "the socket is already bound"),
|
||||||
|
Self::Bound(bound),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match unbound.bind(addr) {
|
||||||
|
Ok(bound) => Ok(Self::Bound(bound)),
|
||||||
|
Err((err, unbound)) => Err((err, Self::Unbound(unbound))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
kernel/src/net/socket/netlink/route/unbound.rs
Normal file
35
kernel/src/net/socket/netlink/route/unbound.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use super::bound::BoundNetlinkRoute;
|
||||||
|
use crate::{
|
||||||
|
events::IoEvents,
|
||||||
|
net::socket::netlink::{
|
||||||
|
table::NETLINK_SOCKET_TABLE, NetlinkSocketAddr, StandardNetlinkProtocol,
|
||||||
|
},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) struct UnboundNetlinkRoute {
|
||||||
|
_private: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnboundNetlinkRoute {
|
||||||
|
pub(super) const fn new() -> Self {
|
||||||
|
Self { _private: () }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn bind(
|
||||||
|
self,
|
||||||
|
addr: &NetlinkSocketAddr,
|
||||||
|
) -> core::result::Result<BoundNetlinkRoute, (Error, Self)> {
|
||||||
|
let bound_handle = NETLINK_SOCKET_TABLE
|
||||||
|
.bind(StandardNetlinkProtocol::ROUTE as _, addr)
|
||||||
|
.map_err(|err| (err, self))?;
|
||||||
|
|
||||||
|
Ok(BoundNetlinkRoute::new(bound_handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn check_io_events(&self) -> IoEvents {
|
||||||
|
IoEvents::OUT
|
||||||
|
}
|
||||||
|
}
|
228
kernel/src/net/socket/netlink/table/mod.rs
Normal file
228
kernel/src/net/socket/netlink/table/mod.rs
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use multicast::MulticastGroup;
|
||||||
|
|
||||||
|
use super::addr::{GroupIdSet, NetlinkProtocolId, NetlinkSocketAddr, PortNum, MAX_GROUPS};
|
||||||
|
use crate::{net::socket::netlink::addr::UNSPECIFIED_PORT, prelude::*, util::random::getrandom};
|
||||||
|
|
||||||
|
mod multicast;
|
||||||
|
|
||||||
|
pub(super) static NETLINK_SOCKET_TABLE: NetlinkSocketTable = NetlinkSocketTable::new();
|
||||||
|
|
||||||
|
/// All bound netlink sockets.
|
||||||
|
pub(super) struct NetlinkSocketTable {
|
||||||
|
protocols: [Mutex<Option<ProtocolSocketTable>>; MAX_ALLOWED_PROTOCOL_ID as usize],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NetlinkSocketTable {
|
||||||
|
pub(super) const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
protocols: [const { Mutex::new(None) }; MAX_ALLOWED_PROTOCOL_ID as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new netlink protocol.
|
||||||
|
fn add_new_protocol(&self, protocol_id: NetlinkProtocolId) {
|
||||||
|
if protocol_id >= MAX_ALLOWED_PROTOCOL_ID {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut protocol = self.protocols[protocol_id as usize].lock();
|
||||||
|
if protocol.is_some() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_protocol = ProtocolSocketTable::new(protocol_id);
|
||||||
|
*protocol = Some(new_protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn bind(
|
||||||
|
&self,
|
||||||
|
protocol: NetlinkProtocolId,
|
||||||
|
addr: &NetlinkSocketAddr,
|
||||||
|
) -> Result<BoundHandle> {
|
||||||
|
if protocol >= MAX_ALLOWED_PROTOCOL_ID {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the netlink protocol does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut protocol = self.protocols[protocol as usize].lock();
|
||||||
|
|
||||||
|
let Some(protocol_sockets) = protocol.as_mut() else {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the netlink protocol does not exist")
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol_sockets.bind(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bound socket table of a single netlink protocol.
|
||||||
|
///
|
||||||
|
/// Each table can have bound sockets for unicast
|
||||||
|
/// and at most 32 groups for multicast.
|
||||||
|
struct ProtocolSocketTable {
|
||||||
|
id: NetlinkProtocolId,
|
||||||
|
// TODO: This table should maintain the port number-to-socket relationship
|
||||||
|
// to support both unicast and multicast effectively.
|
||||||
|
unicast_sockets: BTreeSet<PortNum>,
|
||||||
|
multicast_groups: Box<[MulticastGroup]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProtocolSocketTable {
|
||||||
|
/// Creates a new table.
|
||||||
|
fn new(id: NetlinkProtocolId) -> Self {
|
||||||
|
let multicast_groups = (0u32..MAX_GROUPS).map(|_| MulticastGroup::new()).collect();
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
unicast_sockets: BTreeSet::new(),
|
||||||
|
multicast_groups,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Binds a socket to the table.
|
||||||
|
/// Returns the bound handle.
|
||||||
|
///
|
||||||
|
/// The socket will be bound to a port specified by `addr.port()`.
|
||||||
|
/// If `addr.port()` is zero, the kernel will assign a port,
|
||||||
|
/// typically corresponding to the process ID of the current process.
|
||||||
|
/// If the assigned port is already in use,
|
||||||
|
/// this function will try to allocate a random unused port.
|
||||||
|
///
|
||||||
|
/// Additionally, this socket can join one or more multicast groups,
|
||||||
|
/// as specified in `addr.groups()`.
|
||||||
|
fn bind(&mut self, addr: &NetlinkSocketAddr) -> Result<BoundHandle> {
|
||||||
|
let port = if addr.port() != UNSPECIFIED_PORT {
|
||||||
|
addr.port()
|
||||||
|
} else {
|
||||||
|
let mut random_port = current!().pid();
|
||||||
|
while random_port == UNSPECIFIED_PORT || self.unicast_sockets.contains(&random_port) {
|
||||||
|
getrandom(random_port.as_bytes_mut()).unwrap();
|
||||||
|
}
|
||||||
|
random_port
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.unicast_sockets.contains(&port) {
|
||||||
|
return_errno_with_message!(Errno::EADDRINUSE, "the netlink port is already in use");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.unicast_sockets.insert(port);
|
||||||
|
|
||||||
|
for group_id in addr.groups().ids_iter() {
|
||||||
|
let group = &mut self.multicast_groups[group_id as usize];
|
||||||
|
group.add_member(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(BoundHandle::new(self.id, port, addr.groups()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A bound netlink socket address.
|
||||||
|
///
|
||||||
|
/// When dropping a `BoundHandle`,
|
||||||
|
/// the port will be automatically released.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct BoundHandle {
|
||||||
|
protocol: NetlinkProtocolId,
|
||||||
|
port: PortNum,
|
||||||
|
groups: GroupIdSet,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoundHandle {
|
||||||
|
fn new(protocol: NetlinkProtocolId, port: PortNum, groups: GroupIdSet) -> Self {
|
||||||
|
debug_assert_ne!(port, UNSPECIFIED_PORT);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
protocol,
|
||||||
|
port,
|
||||||
|
groups,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) const fn addr(&self) -> NetlinkSocketAddr {
|
||||||
|
NetlinkSocketAddr::new(self.port, self.groups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for BoundHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
let mut protocol_sockets = NETLINK_SOCKET_TABLE.protocols[self.protocol as usize].lock();
|
||||||
|
|
||||||
|
let Some(protocol_sockets) = protocol_sockets.as_mut() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
protocol_sockets.unicast_sockets.remove(&self.port);
|
||||||
|
|
||||||
|
for group_id in self.groups.ids_iter() {
|
||||||
|
let group = &mut protocol_sockets.multicast_groups[group_id as usize];
|
||||||
|
group.remove_member(self.port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn init() {
|
||||||
|
for protocol in 0..MAX_ALLOWED_PROTOCOL_ID {
|
||||||
|
if is_standard_protocol(protocol) {
|
||||||
|
NETLINK_SOCKET_TABLE.add_new_protocol(protocol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the `protocol` is valid.
|
||||||
|
pub fn is_valid_protocol(protocol: NetlinkProtocolId) -> bool {
|
||||||
|
protocol < MAX_ALLOWED_PROTOCOL_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the `protocol` is reserved for system use.
|
||||||
|
fn is_standard_protocol(protocol: NetlinkProtocolId) -> bool {
|
||||||
|
StandardNetlinkProtocol::try_from(protocol).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Netlink protocols that are assigned for specific usage.
|
||||||
|
///
|
||||||
|
/// Reference: <https://elixir.bootlin.com/linux/v6.0.9/source/include/uapi/linux/netlink.h#L9>.
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy, TryFromInt)]
|
||||||
|
pub enum StandardNetlinkProtocol {
|
||||||
|
/// Routing/device hook
|
||||||
|
ROUTE = 0,
|
||||||
|
/// Unused number
|
||||||
|
UNUSED = 1,
|
||||||
|
/// Reserved for user mode socket protocols
|
||||||
|
USERSOCK = 2,
|
||||||
|
/// Unused number, formerly ip_queue
|
||||||
|
FIREWALL = 3,
|
||||||
|
/// Socket monitoring
|
||||||
|
SOCK_DIAG = 4,
|
||||||
|
/// Netfilter/iptables ULOG
|
||||||
|
NFLOG = 5,
|
||||||
|
/// IPsec
|
||||||
|
XFRM = 6,
|
||||||
|
/// SELinux event notifications
|
||||||
|
SELINUX = 7,
|
||||||
|
/// Open-iSCSI
|
||||||
|
ISCSI = 8,
|
||||||
|
/// Auditing
|
||||||
|
AUDIT = 9,
|
||||||
|
FIB_LOOKUP = 10,
|
||||||
|
CONNECTOR = 11,
|
||||||
|
/// Netfilter subsystem
|
||||||
|
NETFILTER = 12,
|
||||||
|
IP6_FW = 13,
|
||||||
|
/// DECnet routing messages
|
||||||
|
DNRTMSG = 14,
|
||||||
|
/// Kernel messages to userspace
|
||||||
|
KOBJECT_UEVENT = 15,
|
||||||
|
GENERIC = 16,
|
||||||
|
/// Leave room for NETLINK_DM (DM Events)
|
||||||
|
/// SCSI Transports
|
||||||
|
SCSITRANSPORT = 18,
|
||||||
|
ECRYPTFS = 19,
|
||||||
|
RDMA = 20,
|
||||||
|
/// Crypto layer
|
||||||
|
CRYPTO = 21,
|
||||||
|
/// SMC monitoring
|
||||||
|
SMC = 22,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_ALLOWED_PROTOCOL_ID: NetlinkProtocolId = 32;
|
38
kernel/src/net/socket/netlink/table/multicast.rs
Normal file
38
kernel/src/net/socket/netlink/table/multicast.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use crate::{net::socket::netlink::addr::PortNum, prelude::*};
|
||||||
|
|
||||||
|
/// A netlink multicast group.
|
||||||
|
///
|
||||||
|
/// A group can contain multiple sockets,
|
||||||
|
/// each identified by its bound port number.
|
||||||
|
pub struct MulticastGroup {
|
||||||
|
members: BTreeSet<PortNum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MulticastGroup {
|
||||||
|
/// Creates a new multicast group.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
members: BTreeSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the group contains a member.
|
||||||
|
#[expect(unused)]
|
||||||
|
pub fn contains_member(&self, port_num: PortNum) -> bool {
|
||||||
|
self.members.contains(&port_num)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new member to the multicast group.
|
||||||
|
pub fn add_member(&mut self, port_num: PortNum) {
|
||||||
|
debug_assert!(!self.members.contains(&port_num));
|
||||||
|
self.members.insert(port_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes a member from the multicast group.
|
||||||
|
pub fn remove_member(&mut self, port_num: PortNum) {
|
||||||
|
debug_assert!(self.members.contains(&port_num));
|
||||||
|
self.members.remove(&port_num);
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
use aster_bigtcp::wire::{Ipv4Address, PortNum};
|
use aster_bigtcp::wire::{Ipv4Address, PortNum};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
net::socket::{unix::UnixSocketAddr, vsock::addr::VsockSocketAddr},
|
net::socket::{netlink::NetlinkSocketAddr, unix::UnixSocketAddr, vsock::addr::VsockSocketAddr},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -11,5 +11,6 @@ use crate::{
|
|||||||
pub enum SocketAddr {
|
pub enum SocketAddr {
|
||||||
Unix(UnixSocketAddr),
|
Unix(UnixSocketAddr),
|
||||||
IPv4(Ipv4Address, PortNum),
|
IPv4(Ipv4Address, PortNum),
|
||||||
|
Netlink(NetlinkSocketAddr),
|
||||||
Vsock(VsockSocketAddr),
|
Vsock(VsockSocketAddr),
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ pub use stream::VsockStreamSocket;
|
|||||||
// init static driver
|
// init static driver
|
||||||
pub static VSOCK_GLOBAL: Once<Arc<VsockSpace>> = Once::new();
|
pub static VSOCK_GLOBAL: Once<Arc<VsockSpace>> = Once::new();
|
||||||
|
|
||||||
pub fn init() {
|
pub(in crate::net) fn init() {
|
||||||
if let Some(driver) = get_device(DEVICE_NAME) {
|
if let Some(driver) = get_device(DEVICE_NAME) {
|
||||||
VSOCK_GLOBAL.call_once(|| Arc::new(VsockSpace::new(driver)));
|
VSOCK_GLOBAL.call_once(|| Arc::new(VsockSpace::new(driver)));
|
||||||
register_recv_callback(DEVICE_NAME, || {
|
register_recv_callback(DEVICE_NAME, || {
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
fs::{file_handle::FileLike, file_table::FdFlags},
|
fs::{file_handle::FileLike, file_table::FdFlags},
|
||||||
net::socket::{
|
net::socket::{
|
||||||
ip::{datagram::DatagramSocket, stream::StreamSocket},
|
ip::{datagram::DatagramSocket, stream::StreamSocket},
|
||||||
|
netlink::{is_valid_protocol, NetlinkRouteSocket, StandardNetlinkProtocol},
|
||||||
unix::UnixStreamSocket,
|
unix::UnixStreamSocket,
|
||||||
vsock::VsockStreamSocket,
|
vsock::VsockStreamSocket,
|
||||||
},
|
},
|
||||||
@ -16,29 +17,62 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32, ctx: &Context) -> Resu
|
|||||||
let domain = CSocketAddrFamily::try_from(domain)?;
|
let domain = CSocketAddrFamily::try_from(domain)?;
|
||||||
let sock_type = SockType::try_from(type_ & SOCK_TYPE_MASK)?;
|
let sock_type = SockType::try_from(type_ & SOCK_TYPE_MASK)?;
|
||||||
let sock_flags = SockFlags::from_bits_truncate(type_ & !SOCK_TYPE_MASK);
|
let sock_flags = SockFlags::from_bits_truncate(type_ & !SOCK_TYPE_MASK);
|
||||||
let protocol = Protocol::try_from(protocol)?;
|
|
||||||
debug!(
|
debug!(
|
||||||
"domain = {:?}, sock_type = {:?}, sock_flags = {:?}, protocol = {:?}",
|
"domain = {:?}, sock_type = {:?}, sock_flags = {:?}",
|
||||||
domain, sock_type, sock_flags, protocol
|
domain, sock_type, sock_flags
|
||||||
);
|
);
|
||||||
let nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
let is_nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||||
let file_like = match (domain, sock_type, protocol) {
|
let file_like = match (domain, sock_type) {
|
||||||
// FIXME: SOCK_SEQPACKET is added to run fcntl_test, not supported yet.
|
// FIXME: SOCK_SEQPACKET is added to run fcntl_test, not supported yet.
|
||||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM | SockType::SOCK_SEQPACKET, _) => {
|
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM | SockType::SOCK_SEQPACKET) => {
|
||||||
UnixStreamSocket::new(nonblocking) as Arc<dyn FileLike>
|
UnixStreamSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||||
}
|
}
|
||||||
(
|
(CSocketAddrFamily::AF_INET, SockType::SOCK_STREAM) => {
|
||||||
CSocketAddrFamily::AF_INET,
|
let protocol = Protocol::try_from(protocol)?;
|
||||||
SockType::SOCK_STREAM,
|
debug!("protocol = {:?}", protocol);
|
||||||
Protocol::IPPROTO_IP | Protocol::IPPROTO_TCP,
|
match protocol {
|
||||||
) => StreamSocket::new(nonblocking) as Arc<dyn FileLike>,
|
Protocol::IPPROTO_IP | Protocol::IPPROTO_TCP => {
|
||||||
(
|
StreamSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||||
CSocketAddrFamily::AF_INET,
|
}
|
||||||
SockType::SOCK_DGRAM,
|
_ => return_errno_with_message!(Errno::EAFNOSUPPORT, "unsupported protocol"),
|
||||||
Protocol::IPPROTO_IP | Protocol::IPPROTO_UDP,
|
}
|
||||||
) => DatagramSocket::new(nonblocking) as Arc<dyn FileLike>,
|
}
|
||||||
(CSocketAddrFamily::AF_VSOCK, SockType::SOCK_STREAM, _) => {
|
(CSocketAddrFamily::AF_INET, SockType::SOCK_DGRAM) => {
|
||||||
Arc::new(VsockStreamSocket::new(nonblocking)) as Arc<dyn FileLike>
|
let protocol = Protocol::try_from(protocol)?;
|
||||||
|
debug!("protocol = {:?}", protocol);
|
||||||
|
match protocol {
|
||||||
|
Protocol::IPPROTO_IP | Protocol::IPPROTO_UDP => {
|
||||||
|
DatagramSocket::new(is_nonblocking) as Arc<dyn FileLike>
|
||||||
|
}
|
||||||
|
_ => return_errno_with_message!(Errno::EAFNOSUPPORT, "unsupported protocol"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(CSocketAddrFamily::AF_NETLINK, SockType::SOCK_RAW | SockType::SOCK_DGRAM) => {
|
||||||
|
let netlink_family = StandardNetlinkProtocol::try_from(protocol as u32);
|
||||||
|
debug!("netlink family = {:?}", netlink_family);
|
||||||
|
match netlink_family {
|
||||||
|
Ok(StandardNetlinkProtocol::ROUTE) => {
|
||||||
|
Arc::new(NetlinkRouteSocket::new(is_nonblocking))
|
||||||
|
}
|
||||||
|
Ok(_) => {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EAFNOSUPPORT,
|
||||||
|
"some standard netlink families are not supported yet"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
if is_valid_protocol(protocol as u32) {
|
||||||
|
return_errno_with_message!(
|
||||||
|
Errno::EAFNOSUPPORT,
|
||||||
|
"user-provided netlink family is not supported"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return_errno_with_message!(Errno::EAFNOSUPPORT, "invalid netlink family");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(CSocketAddrFamily::AF_VSOCK, SockType::SOCK_STREAM) => {
|
||||||
|
Arc::new(VsockStreamSocket::new(is_nonblocking)) as Arc<dyn FileLike>
|
||||||
}
|
}
|
||||||
_ => return_errno_with_message!(Errno::EAFNOSUPPORT, "unsupported domain"),
|
_ => return_errno_with_message!(Errno::EAFNOSUPPORT, "unsupported domain"),
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,7 @@ use core::cmp::min;
|
|||||||
|
|
||||||
use ostd::task::Task;
|
use ostd::task::Task;
|
||||||
|
|
||||||
use super::{ip::CSocketAddrInet, unix, vsock::CSocketAddrVm};
|
use super::{ip::CSocketAddrInet, netlink::CSocketAddrNetlink, unix, vsock::CSocketAddrVm};
|
||||||
use crate::{current_userspace, net::socket::SocketAddr, prelude::*};
|
use crate::{current_userspace, net::socket::SocketAddr, prelude::*};
|
||||||
|
|
||||||
/// Address family.
|
/// Address family.
|
||||||
@ -162,6 +162,13 @@ pub fn read_socket_addr_from_user(addr: Vaddr, addr_len: usize) -> Result<Socket
|
|||||||
let addr = unix::from_c_bytes(&storage.as_bytes()[..addr_len])?;
|
let addr = unix::from_c_bytes(&storage.as_bytes()[..addr_len])?;
|
||||||
SocketAddr::Unix(addr)
|
SocketAddr::Unix(addr)
|
||||||
}
|
}
|
||||||
|
Ok(CSocketAddrFamily::AF_NETLINK) => {
|
||||||
|
if addr_len < size_of::<CSocketAddrNetlink>() {
|
||||||
|
return_errno_with_message!(Errno::EINVAL, "the socket address length is too small");
|
||||||
|
}
|
||||||
|
let addr = CSocketAddrNetlink::from_bytes(storage.as_bytes());
|
||||||
|
SocketAddr::Netlink(addr.into())
|
||||||
|
}
|
||||||
Ok(CSocketAddrFamily::AF_VSOCK) => {
|
Ok(CSocketAddrFamily::AF_VSOCK) => {
|
||||||
if addr_len < size_of::<CSocketAddrVm>() {
|
if addr_len < size_of::<CSocketAddrVm>() {
|
||||||
return_errno_with_message!(Errno::EINVAL, "the socket address length is too small");
|
return_errno_with_message!(Errno::EINVAL, "the socket address length is too small");
|
||||||
@ -238,36 +245,45 @@ pub fn write_socket_addr_with_max_len(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_task = Task::current().unwrap();
|
|
||||||
let user_space = CurrentUserSpace::new(¤t_task);
|
|
||||||
|
|
||||||
let actual_len = match socket_addr {
|
let actual_len = match socket_addr {
|
||||||
SocketAddr::IPv4(addr, port) => {
|
SocketAddr::IPv4(addr, port) => write_c_socket_address_util::<CSocketAddrInet, _>(
|
||||||
let socket_addr = CSocketAddrInet::from((*addr, *port));
|
(*addr, *port),
|
||||||
let actual_len = size_of::<CSocketAddrInet>();
|
|
||||||
let written_len = min(actual_len, max_len as _);
|
|
||||||
user_space.write_bytes(
|
|
||||||
dest,
|
dest,
|
||||||
&mut VmReader::from(&socket_addr.as_bytes()[..written_len]),
|
max_len as usize,
|
||||||
)?;
|
)?,
|
||||||
actual_len
|
|
||||||
}
|
|
||||||
SocketAddr::Unix(addr) => unix::into_c_bytes_and(addr, |bytes| {
|
SocketAddr::Unix(addr) => unix::into_c_bytes_and(addr, |bytes| {
|
||||||
let written_len = min(bytes.len(), max_len as _);
|
let written_len = min(bytes.len(), max_len as _);
|
||||||
user_space.write_bytes(dest, &mut VmReader::from(&bytes[..written_len]))?;
|
current_userspace!().write_bytes(dest, &mut VmReader::from(&bytes[..written_len]))?;
|
||||||
Ok::<usize, Error>(bytes.len())
|
Ok::<usize, Error>(bytes.len())
|
||||||
})?,
|
})?,
|
||||||
|
SocketAddr::Netlink(addr) => {
|
||||||
|
write_c_socket_address_util::<CSocketAddrNetlink, _>(*addr, dest, max_len as usize)?
|
||||||
|
}
|
||||||
SocketAddr::Vsock(addr) => {
|
SocketAddr::Vsock(addr) => {
|
||||||
let socket_addr = CSocketAddrVm::from(*addr);
|
write_c_socket_address_util::<CSocketAddrVm, _>(*addr, dest, max_len as usize)?
|
||||||
let actual_len = size_of::<CSocketAddrVm>();
|
|
||||||
let written_len = min(actual_len, max_len as _);
|
|
||||||
user_space.write_bytes(
|
|
||||||
dest,
|
|
||||||
&mut VmReader::from(&socket_addr.as_bytes()[..written_len]),
|
|
||||||
)?;
|
|
||||||
actual_len
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(actual_len as i32)
|
Ok(actual_len as i32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utility function to write a C socket address to user space.
|
||||||
|
fn write_c_socket_address_util<TCSockAddr: Pod, TSockAddr>(
|
||||||
|
addr: TSockAddr,
|
||||||
|
dest: Vaddr,
|
||||||
|
max_len: usize,
|
||||||
|
) -> Result<usize>
|
||||||
|
where
|
||||||
|
TCSockAddr: From<TSockAddr>,
|
||||||
|
{
|
||||||
|
let c_socket_addr = TCSockAddr::from(addr);
|
||||||
|
let actual_len = size_of::<TCSockAddr>();
|
||||||
|
let written_len = min(actual_len, max_len);
|
||||||
|
|
||||||
|
current_userspace!().write_bytes(
|
||||||
|
dest,
|
||||||
|
&mut VmReader::from(&c_socket_addr.as_bytes()[..written_len]),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(actual_len)
|
||||||
|
}
|
||||||
|
@ -38,6 +38,7 @@ impl From<(Ipv4Address, PortNum)> for CSocketAddrInet {
|
|||||||
|
|
||||||
impl From<CSocketAddrInet> for (Ipv4Address, PortNum) {
|
impl From<CSocketAddrInet> for (Ipv4Address, PortNum) {
|
||||||
fn from(value: CSocketAddrInet) -> Self {
|
fn from(value: CSocketAddrInet) -> Self {
|
||||||
|
debug_assert_eq!(value.sin_family, CSocketAddrFamily::AF_INET as u16);
|
||||||
(value.sin_addr.into(), value.sin_port.into())
|
(value.sin_addr.into(), value.sin_port.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,5 +7,6 @@ pub use family::{
|
|||||||
|
|
||||||
mod family;
|
mod family;
|
||||||
mod ip;
|
mod ip;
|
||||||
|
mod netlink;
|
||||||
mod unix;
|
mod unix;
|
||||||
mod vsock;
|
mod vsock;
|
||||||
|
41
kernel/src/util/net/addr/netlink.rs
Normal file
41
kernel/src/util/net/addr/netlink.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use super::CSocketAddrFamily;
|
||||||
|
use crate::{
|
||||||
|
net::socket::netlink::{GroupIdSet, NetlinkSocketAddr},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Netlink socket address.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, Pod)]
|
||||||
|
pub struct CSocketAddrNetlink {
|
||||||
|
/// Address family (AF_NETLINK).
|
||||||
|
nl_family: u16,
|
||||||
|
/// Pad bytes (always zero).
|
||||||
|
nl_pad: u16,
|
||||||
|
/// Port ID.
|
||||||
|
nl_pid: u32,
|
||||||
|
/// Multicast groups mask.
|
||||||
|
nl_groups: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<NetlinkSocketAddr> for CSocketAddrNetlink {
|
||||||
|
fn from(value: NetlinkSocketAddr) -> Self {
|
||||||
|
Self {
|
||||||
|
nl_family: CSocketAddrFamily::AF_NETLINK as _,
|
||||||
|
nl_pad: 0,
|
||||||
|
nl_pid: value.port(),
|
||||||
|
nl_groups: value.groups().as_u32(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CSocketAddrNetlink> for NetlinkSocketAddr {
|
||||||
|
fn from(value: CSocketAddrNetlink) -> Self {
|
||||||
|
debug_assert_eq!(value.nl_family, CSocketAddrFamily::AF_NETLINK as u16);
|
||||||
|
let port = value.nl_pid;
|
||||||
|
let groups = GroupIdSet::new(value.nl_groups);
|
||||||
|
NetlinkSocketAddr::new(port, groups)
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,7 @@ impl From<VsockSocketAddr> for CSocketAddrVm {
|
|||||||
|
|
||||||
impl From<CSocketAddrVm> for VsockSocketAddr {
|
impl From<CSocketAddrVm> for VsockSocketAddr {
|
||||||
fn from(value: CSocketAddrVm) -> Self {
|
fn from(value: CSocketAddrVm) -> Self {
|
||||||
|
debug_assert_eq!(value.svm_family, CSocketAddrFamily::AF_VSOCK as u16);
|
||||||
Self {
|
Self {
|
||||||
cid: value.svm_cid,
|
cid: value.svm_cid,
|
||||||
port: value.svm_port,
|
port: value.svm_port,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user