fix(net): Fix UDP missing features and miss polls (#1571)
feat(net): 增强UDP套接字功能并修复多个网络问题 - 实现UDP套接字连接模式下的数据包过滤和预连接数据处理 - 添加UDP套接字断开连接支持,包括AF_UNSPEC和端口0处理 - 实现UDP套接字缓冲区大小配置(SO_SNDBUF/SO_RCVBUF) - 添加UDP套接字shutdown功能支持 - 修复UDP发送到0.0.0.0地址的处理逻辑 - 实现UDP套接字ioctl FIONREAD/TIOCOUTQ支持 - 修复UDP recvmsg/sendmsg系统调用实现 - 增强TCP套接字listen和bind端口处理 - 修复网络接口绑定和地址转换逻辑 - 改进readv系统调用对套接字的特殊处理 Signed-off-by: samuka007 <samuka007@dragonos.org>
This commit is contained in:
parent
84368a9419
commit
a04c6c1ca6
|
|
@ -126,10 +126,18 @@ impl Loopback {
|
|||
let buffer = self.queue.pop_front();
|
||||
match buffer {
|
||||
Some(buffer) => {
|
||||
// debug!("lo receive:{:?}", buffer);
|
||||
// log::debug!(
|
||||
// "lo receive: {} bytes, remaining_queue_len={}, self_ptr={:p}",
|
||||
// buffer.len(),
|
||||
// self.queue.len(),
|
||||
// self
|
||||
// );
|
||||
return buffer;
|
||||
}
|
||||
None => {
|
||||
if !self.queue.is_empty() {
|
||||
log::warn!("lo receive: queue not empty but pop_front returned None!");
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
}
|
||||
|
|
@ -141,8 +149,13 @@ impl Loopback {
|
|||
/// - &mut self:自身可变引用
|
||||
/// - buffer:需要发送的数据包
|
||||
pub fn loopback_transmit(&mut self, buffer: Vec<u8>) {
|
||||
// debug!("lo transmit:{:?}", buffer);
|
||||
self.queue.push_back(buffer)
|
||||
// log::debug!(
|
||||
// "lo transmit: {} bytes, queue_len={}, self_ptr={:p}",
|
||||
// buffer.len(),
|
||||
// self.queue.len(),
|
||||
// self
|
||||
// );
|
||||
self.queue.push_back(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -240,10 +253,9 @@ impl phy::Device for LoopbackDriver {
|
|||
let buffer = self.inner.lock().loopback_receive();
|
||||
//receive队列为为空,返回NONE值以通知上层没有可以receive的包
|
||||
if buffer.is_empty() {
|
||||
// log::debug!("lo receive none!");
|
||||
return Option::None;
|
||||
}
|
||||
// log::debug!("lo receive!");
|
||||
// log::debug!("LoopbackDriver::receive() -> packet {} bytes", buffer.len());
|
||||
let rx = LoopbackRxToken { buffer };
|
||||
let tx = LoopbackTxToken {
|
||||
driver: self.clone(),
|
||||
|
|
@ -260,7 +272,6 @@ impl phy::Device for LoopbackDriver {
|
|||
/// ## 返回值
|
||||
/// - 返回一个 `Some`,其中包含一个发送令牌,该令牌包含一个对自身的克隆引用
|
||||
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
||||
// log::debug!("lo transmit!");
|
||||
Some(LoopbackTxToken {
|
||||
driver: self.clone(),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -266,11 +266,10 @@ impl IfaceCommon {
|
|||
let mut interface = self.smol_iface.lock_irqsave();
|
||||
|
||||
let (has_events, poll_at) = {
|
||||
let poll_result = interface.poll(timestamp, device, &mut sockets);
|
||||
|
||||
(
|
||||
matches!(
|
||||
interface.poll(timestamp, device, &mut sockets),
|
||||
smoltcp::iface::PollResult::SocketStateChanged
|
||||
),
|
||||
matches!(poll_result, smoltcp::iface::PollResult::SocketStateChanged),
|
||||
loop {
|
||||
let poll_at = interface.poll_at(timestamp, &sockets);
|
||||
let Some(instant) = poll_at else {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ impl Syscall for SysReadVHandle {
|
|||
// IoVecs 会进行用户态检验(包含 len==0 的 iov_base 校验)。
|
||||
let iovecs = unsafe { IoVecs::from_user(iov, count, true) }?;
|
||||
|
||||
// TODO: Here work around, not suppose to read entire buf once
|
||||
use crate::process::ProcessManager;
|
||||
if let Ok(_socket_inode) = ProcessManager::current_pcb().get_socket_inode(fd) {
|
||||
// Socket: read entire message then scatter to iovecs
|
||||
let mut buf = iovecs.new_buf(true);
|
||||
let nread = do_read(fd, &mut buf)?;
|
||||
iovecs.scatter(&buf[..nread])?;
|
||||
return Ok(nread);
|
||||
}
|
||||
|
||||
// Linux: limit per readv() to MAX_RW_COUNT = INT_MAX & ~(PAGE_SIZE-1)
|
||||
let max_rw_count = (i32::MAX as usize) & !(MMArch::PAGE_SIZE - 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -217,6 +217,12 @@ impl From<LinkLayerEndpoint> for SockAddr {
|
|||
impl From<Endpoint> for SockAddr {
|
||||
fn from(value: Endpoint) -> Self {
|
||||
match value {
|
||||
Endpoint::Unspecified => Self {
|
||||
addr_ph: SockAddrPlaceholder {
|
||||
family: 0, // AF_UNSPEC
|
||||
data: [0; 14],
|
||||
},
|
||||
},
|
||||
Endpoint::LinkLayer(link_layer_endpoint) => Self::from(link_layer_endpoint),
|
||||
Endpoint::Ip(endpoint) => Self::from(endpoint),
|
||||
Endpoint::Unix(unix_endpoint) => Self::from(unix_endpoint),
|
||||
|
|
@ -257,11 +263,11 @@ impl SockAddr {
|
|||
AddressFamily::INet => {
|
||||
// 下限检查:至少需要包含完整的 sockaddr_in 结构体
|
||||
if len < size_of::<SockAddrIn>() as u32 {
|
||||
log::error!(
|
||||
"len {} < sizeof(sockaddr_in) {}",
|
||||
len,
|
||||
size_of::<SockAddrIn>()
|
||||
);
|
||||
// log::error!(
|
||||
// "len {} < sizeof(sockaddr_in) {}",
|
||||
// len,
|
||||
// size_of::<SockAddrIn>()
|
||||
// );
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
|
|
@ -275,6 +281,12 @@ impl SockAddr {
|
|||
|
||||
return Ok(Endpoint::Ip(wire::IpEndpoint::new(ip, port)));
|
||||
}
|
||||
|
||||
AddressFamily::Unspecified => {
|
||||
// AF_UNSPEC is used to disconnect sockets
|
||||
Ok(Endpoint::Unspecified)
|
||||
}
|
||||
|
||||
AddressFamily::INet6 => {
|
||||
// 下限检查:至少需要包含完整的 sockaddr_in6 结构体
|
||||
if len < size_of::<SockAddrIn6>() as u32 {
|
||||
|
|
|
|||
|
|
@ -44,12 +44,27 @@ pub trait Socket: PollableInode + IndexNode {
|
|||
|
||||
fn send_buffer_size(&self) -> usize;
|
||||
fn recv_buffer_size(&self) -> usize;
|
||||
|
||||
/// # `recv_bytes_available`
|
||||
/// Get the number of bytes currently available to read from the socket.
|
||||
/// Returns 0 by default for socket types that don't track this.
|
||||
fn recv_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Err(SystemError::ENOTTY)
|
||||
}
|
||||
|
||||
/// # `send_bytes_available`
|
||||
/// Get the number of bytes currently available to write to the socket.
|
||||
/// Returns 0 by default for socket types that don't track this.
|
||||
fn send_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Err(SystemError::ENOTTY)
|
||||
}
|
||||
|
||||
/// # `accept`
|
||||
/// 接受连接,仅用于listening stream socket
|
||||
/// ## Block
|
||||
/// 如果没有连接到来,会阻塞
|
||||
fn accept(&self) -> Result<(Arc<dyn Socket>, Endpoint), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
Err(SystemError::EOPNOTSUPP_OR_ENOTSUP)
|
||||
}
|
||||
|
||||
/// # `bind`
|
||||
|
|
@ -96,7 +111,7 @@ pub trait Socket: PollableInode + IndexNode {
|
|||
/// # `listen`
|
||||
/// 监听socket,仅用于stream socket
|
||||
fn listen(&self, _backlog: usize) -> Result<(), SystemError> {
|
||||
Err(SystemError::ENOSYS)
|
||||
Err(SystemError::EOPNOTSUPP_OR_ENOTSUP)
|
||||
}
|
||||
|
||||
// poll
|
||||
|
|
|
|||
|
|
@ -31,14 +31,9 @@ impl TryFrom<usize> for ShutdownBit {
|
|||
// Linux/POSIX shutdown(2):
|
||||
// 0 = SHUT_RD, 1 = SHUT_WR, 2 = SHUT_RDWR
|
||||
match value {
|
||||
0 => Ok(ShutdownBit {
|
||||
bit: Self::RCV_SHUTDOWN,
|
||||
}),
|
||||
1 => Ok(ShutdownBit {
|
||||
bit: Self::SEND_SHUTDOWN,
|
||||
}),
|
||||
2 => Ok(ShutdownBit {
|
||||
bit: Self::RCV_SHUTDOWN | Self::SEND_SHUTDOWN,
|
||||
// SHUT_RD = 0, SHUT_WR = 1, SHUT_RDWR = 2
|
||||
0..=2 => Ok(ShutdownBit {
|
||||
bit: value as u8 + 1,
|
||||
}),
|
||||
_ => Err(Self::Error::EINVAL),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ pub use smoltcp::wire::IpEndpoint;
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Endpoint {
|
||||
/// 未指定端点 (AF_UNSPEC) - 用于UDP断开连接
|
||||
Unspecified,
|
||||
/// 链路层端点
|
||||
LinkLayer(LinkLayerEndpoint),
|
||||
/// 网络层端点
|
||||
|
|
@ -95,6 +97,7 @@ impl From<IpEndpoint> for Endpoint {
|
|||
impl Endpoint {
|
||||
fn sockaddr_len(&self) -> Result<u32, system_error::SystemError> {
|
||||
match self {
|
||||
Endpoint::Unspecified => Ok(SockAddr::from(self.clone()).len()?),
|
||||
Endpoint::LinkLayer(_) => Ok(SockAddr::from(self.clone()).len()?),
|
||||
Endpoint::Ip(_) => Ok(SockAddr::from(self.clone()).len()?),
|
||||
Endpoint::Netlink(_) => Ok(SockAddr::from(self.clone()).len()?),
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ impl BoundInner {
|
|||
});
|
||||
} else {
|
||||
let iface = get_iface_to_bind(address, netns.clone()).ok_or(SystemError::ENODEV)?;
|
||||
// log::debug!(
|
||||
// "BoundInner::bind: binding to iface {} for address {:?}",
|
||||
// iface.iface_name(),
|
||||
// address
|
||||
// );
|
||||
let handle = iface.sockets().lock().add(socket);
|
||||
return Ok(Self {
|
||||
handle,
|
||||
|
|
@ -154,15 +159,26 @@ pub fn get_iface_to_bind(
|
|||
) -> Option<Arc<dyn Iface>> {
|
||||
// log::debug!("get_iface_to_bind: {:?}", ip_addr);
|
||||
// if ip_addr.is_unspecified()
|
||||
netns
|
||||
let result = netns
|
||||
.device_list()
|
||||
.iter()
|
||||
.find(|(_, iface)| {
|
||||
let guard = iface.smol_iface().lock();
|
||||
// log::debug!("iface name: {}, ip: {:?}", iface.iface_name(), guard.ip_addrs());
|
||||
// log::debug!(
|
||||
// " checking iface: {}, ip: {:?}, has_addr={}",
|
||||
// iface.iface_name(),
|
||||
// guard.ip_addrs(),
|
||||
// guard.has_ip_addr(*ip_addr)
|
||||
// );
|
||||
return guard.has_ip_addr(*ip_addr);
|
||||
})
|
||||
.map(|(_, iface)| iface.clone())
|
||||
.map(|(_, iface)| iface.clone());
|
||||
|
||||
// log::debug!(
|
||||
// "get_iface_to_bind: returning iface {:?}",
|
||||
// result.as_ref().map(|i| i.iface_name())
|
||||
// );
|
||||
result
|
||||
}
|
||||
|
||||
/// Get a suitable iface to deal with sendto/connect request if the socket is not bound to an iface.
|
||||
|
|
|
|||
|
|
@ -12,8 +12,12 @@ use crate::{
|
|||
pub type SmolUdpSocket = smoltcp::socket::udp::Socket<'static>;
|
||||
|
||||
pub const DEFAULT_METADATA_BUF_SIZE: usize = 1024;
|
||||
pub const DEFAULT_RX_BUF_SIZE: usize = 64 * 1024;
|
||||
pub const DEFAULT_TX_BUF_SIZE: usize = 64 * 1024;
|
||||
// UDP maximum datagram size is 65507 bytes (65535 - 8 byte UDP header - 20 byte IP header)
|
||||
// Set buffer sizes to accommodate this plus some overhead
|
||||
pub const DEFAULT_RX_BUF_SIZE: usize = 128 * 1024; // 128 KB
|
||||
pub const DEFAULT_TX_BUF_SIZE: usize = 128 * 1024; // 128 KB
|
||||
// Minimum buffer size (Linux uses 256 bytes minimum)
|
||||
pub const MIN_BUF_SIZE: usize = 256;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UnboundUdp {
|
||||
|
|
@ -22,13 +26,53 @@ pub struct UnboundUdp {
|
|||
|
||||
impl UnboundUdp {
|
||||
pub fn new() -> Self {
|
||||
Self::new_with_buf_size(0, 0)
|
||||
}
|
||||
|
||||
pub fn new_with_buf_size(rx_size: usize, tx_size: usize) -> Self {
|
||||
// Buffer sizing strategy:
|
||||
// - setsockopt(SO_RCVBUF, X) stores X
|
||||
// - getsockopt(SO_RCVBUF) returns 2*X (Linux convention)
|
||||
// - Actual buffer allocation: 2*X
|
||||
//
|
||||
// This is a straightforward 2x design that matches the getsockopt return value.
|
||||
//
|
||||
// Note: smoltcp's PacketBuffer has separate metadata_ring and payload_ring.
|
||||
// Unlike Linux where sk_buff metadata shares the same buffer space as payload,
|
||||
// smoltcp allocates them independently. This means:
|
||||
// - We allocate 2*X bytes purely for payload (no metadata overhead)
|
||||
// - This may accept more packets than Linux in some edge cases
|
||||
//
|
||||
// Differences from Linux behavior:
|
||||
// - Linux: Buffer space shared between metadata + payload, so effective payload < 2*X
|
||||
// - DragonOS: Full 2*X available for payload (metadata stored separately)
|
||||
|
||||
let rx_buf_size = if rx_size > 0 {
|
||||
rx_size * 2 // Simple 2x allocation
|
||||
} else {
|
||||
DEFAULT_RX_BUF_SIZE
|
||||
};
|
||||
let tx_buf_size = if tx_size > 0 {
|
||||
tx_size * 2 // Simple 2x allocation
|
||||
} else {
|
||||
DEFAULT_TX_BUF_SIZE
|
||||
};
|
||||
|
||||
// log::debug!(
|
||||
// "new_with_buf_size: requested rx={}, tx={} -> allocating rx={}, tx={} (2x)",
|
||||
// rx_size,
|
||||
// tx_size,
|
||||
// rx_buf_size,
|
||||
// tx_buf_size
|
||||
// );
|
||||
|
||||
let rx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![smoltcp::socket::udp::PacketMetadata::EMPTY; DEFAULT_METADATA_BUF_SIZE],
|
||||
vec![0; DEFAULT_RX_BUF_SIZE],
|
||||
vec![0; rx_buf_size],
|
||||
);
|
||||
let tx_buffer = smoltcp::socket::udp::PacketBuffer::new(
|
||||
vec![smoltcp::socket::udp::PacketMetadata::EMPTY; DEFAULT_METADATA_BUF_SIZE],
|
||||
vec![0; DEFAULT_TX_BUF_SIZE],
|
||||
vec![0; tx_buf_size],
|
||||
);
|
||||
let socket = SmolUdpSocket::new(rx_buffer, tx_buffer);
|
||||
|
||||
|
|
@ -43,11 +87,17 @@ impl UnboundUdp {
|
|||
let inner = BoundInner::bind(self.socket, &local_endpoint.addr, netns)?;
|
||||
let bind_addr = local_endpoint.addr;
|
||||
let bind_port = if local_endpoint.port == 0 {
|
||||
inner.port_manager().bind_ephemeral_port(InetTypes::Udp)?
|
||||
let port = inner.port_manager().bind_ephemeral_port(InetTypes::Udp)?;
|
||||
// log::debug!("UnboundUdp::bind: allocated ephemeral port {}", port);
|
||||
port
|
||||
} else {
|
||||
inner
|
||||
.port_manager()
|
||||
.bind_port(InetTypes::Udp, local_endpoint.port)?;
|
||||
// log::debug!(
|
||||
// "UnboundUdp::bind: explicit bind to port {}",
|
||||
// local_endpoint.port
|
||||
// );
|
||||
local_endpoint.port
|
||||
};
|
||||
|
||||
|
|
@ -69,6 +119,8 @@ impl UnboundUdp {
|
|||
Ok(BoundUdp {
|
||||
inner,
|
||||
remote: SpinLock::new(None),
|
||||
explicitly_bound: true,
|
||||
has_preconnect_data: SpinLock::new(false),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -77,13 +129,36 @@ impl UnboundUdp {
|
|||
remote: smoltcp::wire::IpAddress,
|
||||
netns: Arc<NetNamespace>,
|
||||
) -> Result<BoundUdp, SystemError> {
|
||||
// let (addr, port) = (remote.addr, remote.port);
|
||||
let (inner, address) = BoundInner::bind_ephemeral(self.socket, remote, netns)?;
|
||||
let (inner, local_addr) = BoundInner::bind_ephemeral(self.socket, remote, netns)?;
|
||||
let bound_port = inner.port_manager().bind_ephemeral_port(InetTypes::Udp)?;
|
||||
let endpoint = smoltcp::wire::IpEndpoint::new(address, bound_port);
|
||||
// log::debug!(
|
||||
// "UnboundUdp::bind_ephemeral: allocated ephemeral port {} for remote {:?}",
|
||||
// bound_port,
|
||||
// remote
|
||||
// );
|
||||
|
||||
// Bind the smoltcp socket to the local endpoint
|
||||
if local_addr.is_unspecified() {
|
||||
if inner
|
||||
.with_mut::<smoltcp::socket::udp::Socket, _, _>(|socket| socket.bind(bound_port))
|
||||
.is_err()
|
||||
{
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
} else if inner
|
||||
.with_mut::<smoltcp::socket::udp::Socket, _, _>(|socket| {
|
||||
socket.bind(smoltcp::wire::IpEndpoint::new(local_addr, bound_port))
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
Ok(BoundUdp {
|
||||
inner,
|
||||
remote: SpinLock::new(Some(endpoint)),
|
||||
remote: SpinLock::new(None),
|
||||
explicitly_bound: false,
|
||||
has_preconnect_data: SpinLock::new(false),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -92,6 +167,13 @@ impl UnboundUdp {
|
|||
pub struct BoundUdp {
|
||||
inner: BoundInner,
|
||||
remote: SpinLock<Option<smoltcp::wire::IpEndpoint>>,
|
||||
/// True if socket was explicitly bound by user, false if implicitly bound by connect
|
||||
explicitly_bound: bool,
|
||||
/// Whether there were buffered packets at connect time - if true, allow next recv without filtering
|
||||
/// 这是用来模拟 Linux UDP 在应用filter前的行为。在smoltcp下,当有包到来时总是会推送到
|
||||
/// udp socket queue 中,而不是先针对connect进行filter操作。这里做workaround, 当connect是检查是否有包
|
||||
/// 在缓冲区,如果有,第一个包我们走非connect而不是connect的recv方法(即接受第一个非connect对端对应的包)
|
||||
has_preconnect_data: SpinLock<bool>,
|
||||
}
|
||||
|
||||
impl BoundUdp {
|
||||
|
|
@ -123,21 +205,141 @@ impl BoundUdp {
|
|||
}
|
||||
|
||||
pub fn connect(&self, remote: smoltcp::wire::IpEndpoint) {
|
||||
// let _local = self.endpoint();
|
||||
// log::debug!(
|
||||
// "BoundUdp::connect: local={:?}, connecting to remote={:?}",
|
||||
// _local,
|
||||
// remote
|
||||
// );
|
||||
|
||||
// Check if there are buffered packets - if so, allow next recv without filtering
|
||||
let has_buffered = self.with_socket(|socket| socket.can_recv());
|
||||
*self.has_preconnect_data.lock() = has_buffered;
|
||||
// log::debug!("BoundUdp::connect: has pre-connect data = {}", has_buffered);
|
||||
|
||||
self.remote.lock().replace(remote);
|
||||
}
|
||||
|
||||
pub fn disconnect(&self) {
|
||||
self.remote.lock().take();
|
||||
}
|
||||
|
||||
/// Returns true if this socket should be unbound on disconnect
|
||||
pub fn should_unbind_on_disconnect(&self) -> bool {
|
||||
!self.explicitly_bound
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn try_recv(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
peek: bool,
|
||||
) -> Result<(usize, smoltcp::wire::IpEndpoint), SystemError> {
|
||||
let remote = *self.remote.lock();
|
||||
|
||||
self.with_mut_socket(|socket| {
|
||||
if socket.can_recv() {
|
||||
if let Ok((size, metadata)) = socket.recv_slice(buf) {
|
||||
return Ok((size, metadata.endpoint));
|
||||
}
|
||||
// If connected, filter packets by source address (except pre-connect packets)
|
||||
|
||||
let mut has_preconnect_guard = self.has_preconnect_data.lock();
|
||||
let has_preconnect = *has_preconnect_guard;
|
||||
// let has_preconnect = false;
|
||||
if has_preconnect {
|
||||
*has_preconnect_guard = false;
|
||||
}
|
||||
drop(has_preconnect_guard);
|
||||
let should_filter = remote.is_some() && !has_preconnect;
|
||||
if should_filter {
|
||||
let expected_remote = remote.unwrap();
|
||||
// log::debug!("try_recv: connected mode, expected_remote={:?}, buf_len={}, can_recv={}",
|
||||
// expected_remote, buf.len(), socket.can_recv());
|
||||
|
||||
// Loop to skip packets from unexpected sources
|
||||
loop {
|
||||
if !socket.can_recv() {
|
||||
// log::debug!("try_recv: can_recv=false, returning EAGAIN");
|
||||
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
|
||||
}
|
||||
|
||||
// Peek to check source address before receiving
|
||||
// Note: peek() instead of peek_slice() because peek_slice() returns Truncated
|
||||
// error when buffer is smaller than packet, but we still want to receive it
|
||||
match socket.peek() {
|
||||
Ok((payload, metadata)) => {
|
||||
// log::debug!("try_recv: peeked {} bytes from {:?}, buf_len={}", payload.len(), metadata.endpoint, buf.len());
|
||||
if metadata.endpoint == expected_remote {
|
||||
// Source matches
|
||||
|
||||
// Special case: zero-length buffer
|
||||
if buf.is_empty() {
|
||||
// log::debug!("try_recv: zero-length buffer in connected mode, returning 0 bytes");
|
||||
return Ok((0, expected_remote));
|
||||
}
|
||||
|
||||
if peek {
|
||||
// MSG_PEEK: just copy the data we peeked
|
||||
let copy_len = core::cmp::min(buf.len(), payload.len());
|
||||
buf[..copy_len].copy_from_slice(&payload[..copy_len]);
|
||||
// log::debug!("try_recv: peek succeeded, size={}", copy_len);
|
||||
return Ok((copy_len, expected_remote));
|
||||
} else {
|
||||
// Receive the packet
|
||||
let (recv_buf, _metadata) =
|
||||
socket.recv().map_err(|_| SystemError::ENOBUFS)?;
|
||||
let length = core::cmp::min(buf.len(), recv_buf.len());
|
||||
buf[..length].copy_from_slice(&recv_buf[..length]);
|
||||
debug_assert_eq!(expected_remote, _metadata.endpoint);
|
||||
return Ok((length, expected_remote));
|
||||
}
|
||||
} else {
|
||||
// just drop the packet
|
||||
let _ = socket.recv();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(smoltcp::socket::udp::RecvError::Exhausted) => {
|
||||
return Err(SystemError::ENOBUFS)
|
||||
}
|
||||
Err(_e) => return Err(SystemError::EIO),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// log::debug!("try_recv: unconnected mode, buf_len={}, can_recv={}", buf.len(), socket.can_recv());
|
||||
// Not connected, receive from any source
|
||||
|
||||
// Special case: if buffer length is 0, just peek to check if data exists
|
||||
if buf.is_empty() {
|
||||
if socket.can_recv() {
|
||||
// Peek to get the source endpoint without consuming data
|
||||
if let Ok((_payload, metadata)) = socket.peek() {
|
||||
// log::debug!("try_recv: zero-length buffer with data available, returning 0 bytes from {:?}", metadata.endpoint);
|
||||
return Ok((0, metadata.endpoint));
|
||||
}
|
||||
}
|
||||
// log::debug!("try_recv: zero-length buffer with no data, returning EAGAIN");
|
||||
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
|
||||
}
|
||||
|
||||
if socket.can_recv() {
|
||||
if peek {
|
||||
// MSG_PEEK: peek data without consuming
|
||||
if let Ok((payload, metadata)) = socket.peek() {
|
||||
let copy_len = core::cmp::min(buf.len(), payload.len());
|
||||
buf[..copy_len].copy_from_slice(&payload[..copy_len]);
|
||||
// log::debug!("try_recv: unconnected peek succeeded, size={}", copy_len);
|
||||
return Ok((copy_len, metadata.endpoint));
|
||||
}
|
||||
} else {
|
||||
// Receive the packet // Receive the packet
|
||||
let (recv_buf, metadata) =
|
||||
socket.recv().map_err(|_| SystemError::ENOBUFS)?;
|
||||
let length = core::cmp::min(buf.len(), recv_buf.len());
|
||||
buf[..length].copy_from_slice(&recv_buf[..length]);
|
||||
return Ok((length, metadata.endpoint));
|
||||
}
|
||||
}
|
||||
// log::debug!("try_recv: unconnected recv failed, returning EAGAIN");
|
||||
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
|
||||
}
|
||||
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK);
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -146,15 +348,45 @@ impl BoundUdp {
|
|||
buf: &[u8],
|
||||
to: Option<smoltcp::wire::IpEndpoint>,
|
||||
) -> Result<usize, SystemError> {
|
||||
let remote = to.or(*self.remote.lock()).ok_or(SystemError::ENOTCONN)?;
|
||||
let result = self.with_mut_socket(|socket| {
|
||||
if socket.can_send() && socket.send_slice(buf, remote).is_ok() {
|
||||
// log::debug!("send {} bytes", buf.len());
|
||||
return Ok(buf.len());
|
||||
let connected_remote = *self.remote.lock();
|
||||
let mut remote = to.or(connected_remote).ok_or(SystemError::ENOTCONN)?;
|
||||
|
||||
// Validate port - sending to port 0 is invalid
|
||||
if remote.port == 0 {
|
||||
log::warn!("UDP try_send: attempted to send to port 0");
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
// Linux treats sending to 0.0.0.0 (INADDR_ANY) as sending to localhost
|
||||
// smoltcp rejects it as "Unaddressable", so we translate it here
|
||||
if remote.addr.is_unspecified() {
|
||||
remote.addr = smoltcp::wire::IpAddress::v4(127, 0, 0, 1);
|
||||
}
|
||||
|
||||
// log::debug!(
|
||||
// "try_send: sending {} bytes to {:?}, can_send={}",
|
||||
// buf.len(),
|
||||
// remote,
|
||||
// self.with_socket(|socket| socket.can_send())
|
||||
// );
|
||||
|
||||
self.with_mut_socket(|socket| {
|
||||
if socket.can_send() {
|
||||
match socket.send_slice(buf, remote) {
|
||||
Ok(_) => {
|
||||
// log::debug!("try_send: send successful");
|
||||
Ok(buf.len())
|
||||
}
|
||||
Err(_e) => {
|
||||
// log::debug!("try_send: send failed: {:?}", _e);
|
||||
Err(SystemError::ENOBUFS)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// log::debug!("try_send: can_send=false, returning ENOBUFS");
|
||||
Err(SystemError::ENOBUFS)
|
||||
}
|
||||
return Err(SystemError::ENOBUFS);
|
||||
});
|
||||
return result;
|
||||
})
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &BoundInner {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,68 +0,0 @@
|
|||
|
||||
bitflags! {
|
||||
pub struct IpOptions: u32 {
|
||||
const IP_TOS = 1; // Type of service
|
||||
const IP_TTL = 2; // Time to live
|
||||
const IP_HDRINCL = 3; // Header compression
|
||||
const IP_OPTIONS = 4; // IP options
|
||||
const IP_ROUTER_ALERT = 5; // Router alert
|
||||
const IP_RECVOPTS = 6; // Receive options
|
||||
const IP_RETOPTS = 7; // Return options
|
||||
const IP_PKTINFO = 8; // Packet information
|
||||
const IP_PKTOPTIONS = 9; // Packet options
|
||||
const IP_MTU_DISCOVER = 10; // MTU discovery
|
||||
const IP_RECVERR = 11; // Receive errors
|
||||
const IP_RECVTTL = 12; // Receive time to live
|
||||
const IP_RECVTOS = 13; // Receive type of service
|
||||
const IP_MTU = 14; // MTU
|
||||
const IP_FREEBIND = 15; // Freebind
|
||||
const IP_IPSEC_POLICY = 16; // IPsec policy
|
||||
const IP_XFRM_POLICY = 17; // IPipsec transform policy
|
||||
const IP_PASSSEC = 18; // Pass security
|
||||
const IP_TRANSPARENT = 19; // Transparent
|
||||
|
||||
const IP_RECVRETOPTS = 20; // Receive return options (deprecated)
|
||||
|
||||
const IP_ORIGDSTADDR = 21; // Originate destination address (used by TProxy)
|
||||
const IP_RECVORIGDSTADDR = 21; // Receive originate destination address
|
||||
|
||||
const IP_MINTTL = 22; // Minimum time to live
|
||||
const IP_NODEFRAG = 23; // Don't fragment (used by TProxy)
|
||||
const IP_CHECKSUM = 24; // Checksum offload (used by TProxy)
|
||||
const IP_BIND_ADDRESS_NO_PORT = 25; // Bind to address without port (used by TProxy)
|
||||
const IP_RECVFRAGSIZE = 26; // Receive fragment size
|
||||
const IP_RECVERR_RFC4884 = 27; // Receive ICMPv6 error notifications
|
||||
|
||||
const IP_PMTUDISC_DONT = 28; // Don't send DF frames
|
||||
const IP_PMTUDISC_DO = 29; // Always DF
|
||||
const IP_PMTUDISC_PROBE = 30; // Ignore dst pmtu
|
||||
const IP_PMTUDISC_INTERFACE = 31; // Always use interface mtu (ignores dst pmtu)
|
||||
const IP_PMTUDISC_OMIT = 32; // Weaker version of IP_PMTUDISC_INTERFACE
|
||||
|
||||
const IP_MULTICAST_IF = 33; // Multicast interface
|
||||
const IP_MULTICAST_TTL = 34; // Multicast time to live
|
||||
const IP_MULTICAST_LOOP = 35; // Multicast loopback
|
||||
const IP_ADD_MEMBERSHIP = 36; // Add multicast group membership
|
||||
const IP_DROP_MEMBERSHIP = 37; // Drop multicast group membership
|
||||
const IP_UNBLOCK_SOURCE = 38; // Unblock source
|
||||
const IP_BLOCK_SOURCE = 39; // Block source
|
||||
const IP_ADD_SOURCE_MEMBERSHIP = 40; // Add source multicast group membership
|
||||
const IP_DROP_SOURCE_MEMBERSHIP = 41; // Drop source multicast group membership
|
||||
const IP_MSFILTER = 42; // Multicast source filter
|
||||
|
||||
const MCAST_JOIN_GROUP = 43; // Join a multicast group
|
||||
const MCAST_BLOCK_SOURCE = 44; // Block a multicast source
|
||||
const MCAST_UNBLOCK_SOURCE = 45; // Unblock a multicast source
|
||||
const MCAST_LEAVE_GROUP = 46; // Leave a multicast group
|
||||
const MCAST_JOIN_SOURCE_GROUP = 47; // Join a multicast source group
|
||||
const MCAST_LEAVE_SOURCE_GROUP = 48; // Leave a multicast source group
|
||||
const MCAST_MSFILTER = 49; // Multicast source filter
|
||||
|
||||
const IP_MULTICAST_ALL = 50; // Multicast all
|
||||
const IP_UNICAST_IF = 51; // Unicast interface
|
||||
const IP_LOCAL_PORT_RANGE = 52; // Local port range
|
||||
const IP_PROTOCOL = 53; // Protocol
|
||||
|
||||
// ... other flags ...
|
||||
}
|
||||
}
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
pub const SOL_SOCKET: u16 = 1;
|
||||
|
||||
#[derive(Debug, Clone, Copy, FromPrimitive, ToPrimitive, PartialEq, Eq)]
|
||||
pub enum IPProtocol {
|
||||
/// Dummy protocol for TCP.
|
||||
IP = 0,
|
||||
/// Internet Control Message Protocol.
|
||||
ICMP = 1,
|
||||
/// Internet Group Management Protocol.
|
||||
IGMP = 2,
|
||||
/// IPIP tunnels (older KA9Q tunnels use 94).
|
||||
IPIP = 4,
|
||||
/// Transmission Control Protocol.
|
||||
TCP = 6,
|
||||
/// Exterior Gateway Protocol.
|
||||
EGP = 8,
|
||||
/// PUP protocol.
|
||||
PUP = 12,
|
||||
/// User Datagram Protocol.
|
||||
UDP = 17,
|
||||
/// XNS IDP protocol.
|
||||
IDP = 22,
|
||||
/// SO Transport Protocol Class 4.
|
||||
TP = 29,
|
||||
/// Datagram Congestion Control Protocol.
|
||||
DCCP = 33,
|
||||
/// IPv6-in-IPv4 tunnelling.
|
||||
IPv6 = 41,
|
||||
/// RSVP Protocol.
|
||||
RSVP = 46,
|
||||
/// Generic Routing Encapsulation. (Cisco GRE) (rfc 1701, 1702)
|
||||
GRE = 47,
|
||||
/// Encapsulation Security Payload protocol
|
||||
ESP = 50,
|
||||
/// Authentication Header protocol
|
||||
AH = 51,
|
||||
/// Multicast Transport Protocol.
|
||||
MTP = 92,
|
||||
/// IP option pseudo header for BEET
|
||||
BEETPH = 94,
|
||||
/// Encapsulation Header.
|
||||
ENCAP = 98,
|
||||
/// Protocol Independent Multicast.
|
||||
PIM = 103,
|
||||
/// Compression Header Protocol.
|
||||
COMP = 108,
|
||||
/// Stream Control Transport Protocol
|
||||
SCTP = 132,
|
||||
/// UDP-Lite protocol (RFC 3828)
|
||||
UDPLITE = 136,
|
||||
/// MPLS in IP (RFC 4023)
|
||||
MPLSINIP = 137,
|
||||
/// Ethernet-within-IPv6 Encapsulation
|
||||
ETHERNET = 143,
|
||||
/// Raw IP packets
|
||||
RAW = 255,
|
||||
/// Multipath TCP connection
|
||||
MPTCP = 262,
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for IPProtocol {
|
||||
type Error = system_error::SystemError;
|
||||
|
||||
fn try_from(value: u16) -> Result<Self, Self::Error> {
|
||||
match <Self as num_traits::FromPrimitive>::from_u16(value) {
|
||||
Some(p) => Ok(p),
|
||||
None => Err(system_error::SystemError::EPROTONOSUPPORT),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<IPProtocol> for u16 {
|
||||
fn from(value: IPProtocol) -> Self {
|
||||
<IPProtocol as num_traits::ToPrimitive>::to_u16(&value).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
@ -237,6 +237,38 @@ impl crate::net::socket::Socket for RawSocket {
|
|||
_ => Err(SystemError::ENOPROTOOPT),
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
let guard = self.inner.read();
|
||||
Ok(match *guard {
|
||||
Some(RawInner::Wildcard(ref bound)) => {
|
||||
bound.with_mut_socket(|socket| match socket.peek() {
|
||||
Ok(payload) => payload.len(),
|
||||
Err(_) => 0,
|
||||
})
|
||||
}
|
||||
Some(RawInner::Bound(ref bound)) => {
|
||||
bound.with_mut_socket(|socket| match socket.peek() {
|
||||
Ok(payload) => payload.len(),
|
||||
Err(_) => 0,
|
||||
})
|
||||
}
|
||||
_ => 0,
|
||||
})
|
||||
}
|
||||
|
||||
fn send_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
let guard = self.inner.read();
|
||||
Ok(match *guard {
|
||||
Some(RawInner::Wildcard(ref bound)) => {
|
||||
bound.with_socket(|socket| socket.payload_send_capacity() - socket.send_queue())
|
||||
}
|
||||
Some(RawInner::Bound(ref bound)) => {
|
||||
bound.with_socket(|socket| socket.payload_send_capacity() - socket.send_queue())
|
||||
}
|
||||
_ => 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InetSocket for RawSocket {
|
||||
|
|
|
|||
|
|
@ -21,13 +21,18 @@ fn new_smoltcp_socket() -> smoltcp::socket::tcp::Socket<'static> {
|
|||
smoltcp::socket::tcp::Socket::new(rx_buffer, tx_buffer)
|
||||
}
|
||||
|
||||
fn new_listen_smoltcp_socket<T>(local_endpoint: T) -> smoltcp::socket::tcp::Socket<'static>
|
||||
fn new_listen_smoltcp_socket<T>(
|
||||
local_endpoint: T,
|
||||
) -> Result<smoltcp::socket::tcp::Socket<'static>, SystemError>
|
||||
where
|
||||
T: Into<smoltcp::wire::IpListenEndpoint>,
|
||||
{
|
||||
let mut socket = new_smoltcp_socket();
|
||||
socket.listen(local_endpoint).unwrap();
|
||||
socket
|
||||
socket.listen(local_endpoint).map_err(|e| match e {
|
||||
tcp::ListenError::InvalidState => SystemError::EINVAL, // TODO: Check is right impl
|
||||
tcp::ListenError::Unaddressable => SystemError::EADDRINUSE,
|
||||
})?;
|
||||
Ok(socket)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -64,11 +69,20 @@ impl Init {
|
|||
match self {
|
||||
Init::Unbound((socket, _)) => {
|
||||
let bound = socket::inet::BoundInner::bind(*socket, &local_endpoint.addr, netns)?;
|
||||
bound
|
||||
.port_manager()
|
||||
.bind_port(Types::Tcp, local_endpoint.port)?;
|
||||
// bound.iface().common().bind_socket()
|
||||
Ok(Init::Bound((bound, local_endpoint)))
|
||||
|
||||
// Handle ephemeral port assignment (port 0)
|
||||
let bind_port = if local_endpoint.port == 0 {
|
||||
bound.port_manager().bind_ephemeral_port(Types::Tcp)?
|
||||
} else {
|
||||
bound
|
||||
.port_manager()
|
||||
.bind_port(Types::Tcp, local_endpoint.port)?;
|
||||
local_endpoint.port
|
||||
};
|
||||
|
||||
// Create endpoint with actual assigned port
|
||||
let final_endpoint = smoltcp::wire::IpEndpoint::new(local_endpoint.addr, bind_port);
|
||||
Ok(Init::Bound((bound, final_endpoint)))
|
||||
}
|
||||
Init::Bound(_) => {
|
||||
log::debug!("Already Bound");
|
||||
|
|
@ -138,13 +152,26 @@ impl Init {
|
|||
} else {
|
||||
smoltcp::wire::IpListenEndpoint::from(local)
|
||||
};
|
||||
log::debug!("listen at {:?}", listen_addr);
|
||||
if listen_addr.port == 0 {
|
||||
// Invalid port number
|
||||
return Err((Init::Bound((inner, local)), SystemError::EINVAL));
|
||||
}
|
||||
// log::debug!("listen at {:?}, backlog {}", listen_addr, backlog);
|
||||
if backlog == 0 || backlog > u16::MAX as usize {
|
||||
// Invalid backlog value
|
||||
return Err((Init::Bound((inner, local)), SystemError::EINVAL));
|
||||
}
|
||||
|
||||
// FIXME: need refactor backlog mechanism for large number of backlog
|
||||
let backlog = if backlog > 8 { 8 } else { backlog };
|
||||
|
||||
let mut inners = Vec::new();
|
||||
if let Err(err) = || -> Result<(), SystemError> {
|
||||
for _ in 0..(backlog - 1) {
|
||||
for _i in 0..(backlog - 1) {
|
||||
// -1 because the first one is already bound
|
||||
// log::debug!("loop {:?}", _i);
|
||||
let new_listen = socket::inet::BoundInner::bind(
|
||||
new_listen_smoltcp_socket(listen_addr),
|
||||
new_listen_smoltcp_socket(listen_addr)?,
|
||||
listen_addr
|
||||
.addr
|
||||
.as_ref()
|
||||
|
|
@ -155,6 +182,7 @@ impl Init {
|
|||
)?;
|
||||
inners.push(new_listen);
|
||||
}
|
||||
// log::debug!("finished listen");
|
||||
Ok(())
|
||||
}() {
|
||||
return Err((Init::Bound((inner, local)), err));
|
||||
|
|
@ -320,7 +348,7 @@ impl Listening {
|
|||
// log::debug!("local at {:?}", local_endpoint);
|
||||
|
||||
let mut new_listen = socket::inet::BoundInner::bind(
|
||||
new_listen_smoltcp_socket(self.listen_addr),
|
||||
new_listen_smoltcp_socket(self.listen_addr)?,
|
||||
self.listen_addr
|
||||
.addr
|
||||
.as_ref()
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ use crate::net::socket::IFNAMSIZ;
|
|||
// Socket ioctl commands
|
||||
const SIOCGIFCONF: u32 = 0x8912; // Get interface list
|
||||
const SIOCGIFINDEX: u32 = 0x8933; // name -> if_index mapping
|
||||
const FIONREAD: u32 = 0x541B; // Get number of bytes available to read
|
||||
const TIOCOUTQ: u32 = 0x5411; // Get output queue size
|
||||
|
||||
/// ## ifreq - Interface request structure
|
||||
/// Used for socket ioctls. Must match C struct layout.
|
||||
|
|
@ -325,6 +327,13 @@ impl<T: Socket + 'static> IndexNode for T {
|
|||
buf: &[u8],
|
||||
data: SpinLockGuard<FilePrivateData>,
|
||||
) -> Result<usize, SystemError> {
|
||||
if buf.is_empty() {
|
||||
log::debug!(
|
||||
"Socket write_at: ZERO-LENGTH write, buf.len()={}, _len={}",
|
||||
buf.len(),
|
||||
_len
|
||||
);
|
||||
}
|
||||
drop(data);
|
||||
self.write(buf)
|
||||
}
|
||||
|
|
@ -356,17 +365,47 @@ impl<T: Socket + 'static> IndexNode for T {
|
|||
Ok(md)
|
||||
}
|
||||
|
||||
// TODO: implement ioctl for socket
|
||||
/// 这里应该实现 通用 Socket 作为 IndexNode 的 ioctl 选项
|
||||
/// 对于协议特定的 ioctl 选项实现,请在各个 Socket impl trait 内实现
|
||||
///
|
||||
/// ## 层级结构
|
||||
///
|
||||
/// `dyn IndexNode::ioctl` -> `impl IndexNode for T: Socket` -> `dyn Socket::ioctl`
|
||||
///
|
||||
/// Socket trait 的 ioctl 覆盖了 IndexNode 这一层的调用,但由于 `impl IndexNode for T: Socket`,
|
||||
/// 我们先调用在 IndexNode 这一层为 Socket 默认实现的 ioctl,再调用 `Socket` trait 内
|
||||
/// 的 ioctl
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
data: usize,
|
||||
private_data: &FilePrivateData,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
match cmd {
|
||||
SIOCGIFCONF => handle_siocgifconf(data),
|
||||
SIOCGIFINDEX => handle_siocgifindex(data),
|
||||
_ => Socket::ioctl(self, cmd, data, private_data),
|
||||
FIONREAD /* TIOCINQ */ => {
|
||||
// Get number of bytes available to read
|
||||
let bytes_available = self.recv_bytes_available()?;
|
||||
let mut writer =
|
||||
UserBufferWriter::new(data as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
let to_write = core::cmp::min(bytes_available, i32::MAX as usize) as i32;
|
||||
writer.buffer_protected(0)?.write_one::<i32>(0, &to_write)?;
|
||||
Ok(0)
|
||||
}
|
||||
TIOCOUTQ => {
|
||||
// Get number of bytes available to write
|
||||
let bytes_available = self.send_bytes_available()?;
|
||||
let mut writer =
|
||||
UserBufferWriter::new(data as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
let to_write = core::cmp::min(bytes_available, i32::MAX as usize) as i32;
|
||||
writer.buffer_protected(0)?.write_one::<i32>(0, &to_write)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => {
|
||||
// 透穿调用子协议栈的ioctl
|
||||
Socket::ioctl(self, cmd, data, _private_data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use crate::{
|
||||
filesystem::epoll::{event_poll::EventPoll, EPollEventType},
|
||||
filesystem::vfs::iov::IoVecs,
|
||||
filesystem::vfs::{
|
||||
fasync::FAsyncItems, utils::DName, vcore::generate_inode_id, FilePrivateData, InodeId,
|
||||
},
|
||||
filesystem::vfs::{fasync::FAsyncItems, utils::DName, vcore::generate_inode_id, InodeId},
|
||||
libs::rwlock::RwLock,
|
||||
libs::spinlock::SpinLock,
|
||||
libs::wait_queue::WaitQueue,
|
||||
|
|
@ -30,7 +28,6 @@ use system_error::SystemError;
|
|||
use crate::time::Duration;
|
||||
|
||||
use crate::process::namespace::net_namespace::NetNamespace;
|
||||
use crate::syscall::user_access::UserBufferWriter;
|
||||
use crate::{
|
||||
filesystem::vfs::file::File,
|
||||
net::socket::unix::{current_ucred, nobody_ucred, UCred},
|
||||
|
|
@ -38,15 +35,6 @@ use crate::{
|
|||
syscall::user_access::UserBufferReader,
|
||||
};
|
||||
|
||||
// Socket ioctls used by gVisor unix socket tests.
|
||||
const TIOCOUTQ: u32 = 0x5411; // Get output queue size
|
||||
const FIONREAD: u32 = 0x541B; // Get input queue size (aka TIOCINQ)
|
||||
const SIOCGIFINDEX: u32 = 0x8933; // name -> if_index mapping
|
||||
|
||||
fn clamp_usize_to_i32(v: usize) -> i32 {
|
||||
core::cmp::min(v, i32::MAX as usize) as i32
|
||||
}
|
||||
|
||||
// Use common ancillary message types from parent module
|
||||
use super::{cmsg_align, CmsgBuffer, Cmsghdr, MSG_CTRUNC, SCM_CREDENTIALS, SCM_RIGHTS, SOL_SOCKET};
|
||||
|
||||
|
|
@ -682,38 +670,12 @@ impl Socket for UnixDatagramSocket {
|
|||
&self.open_files
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
arg: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
if arg == 0 {
|
||||
return Err(SystemError::EFAULT);
|
||||
}
|
||||
fn recv_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Ok(self.ioctl_fionread())
|
||||
}
|
||||
|
||||
match cmd {
|
||||
FIONREAD => {
|
||||
let available = self.ioctl_fionread();
|
||||
let mut writer =
|
||||
UserBufferWriter::new(arg as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
writer
|
||||
.buffer_protected(0)?
|
||||
.write_one::<i32>(0, &clamp_usize_to_i32(available))?;
|
||||
Ok(0)
|
||||
}
|
||||
TIOCOUTQ => {
|
||||
let queued = self.ioctl_tiocoutq();
|
||||
let mut writer =
|
||||
UserBufferWriter::new(arg as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
writer
|
||||
.buffer_protected(0)?
|
||||
.write_one::<i32>(0, &clamp_usize_to_i32(queued))?;
|
||||
Ok(0)
|
||||
}
|
||||
SIOCGIFINDEX => Err(SystemError::ENODEV),
|
||||
_ => Err(SystemError::ENOSYS),
|
||||
}
|
||||
fn send_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Ok(self.ioctl_tiocoutq())
|
||||
}
|
||||
|
||||
fn set_nonblocking(&self, nonblocking: bool) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
filesystem::epoll::{event_poll::EventPoll, EPollEventType},
|
||||
filesystem::vfs::{fasync::FAsyncItems, vcore::generate_inode_id, FilePrivateData, InodeId},
|
||||
filesystem::vfs::{fasync::FAsyncItems, vcore::generate_inode_id, InodeId},
|
||||
libs::rwlock::RwLock,
|
||||
net::socket::{self, *},
|
||||
};
|
||||
|
|
@ -29,21 +29,12 @@ use crate::filesystem::vfs::iov::IoVecs;
|
|||
use crate::net::socket::unix::{current_ucred, nobody_ucred, UCred};
|
||||
use crate::process::namespace::net_namespace::NetNamespace;
|
||||
use crate::process::ProcessManager;
|
||||
use crate::syscall::user_access::{UserBufferReader, UserBufferWriter};
|
||||
use crate::syscall::user_access::UserBufferReader;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
// Use common ancillary message types from parent module
|
||||
use super::{cmsg_align, CmsgBuffer, Cmsghdr, MSG_CTRUNC, SCM_CREDENTIALS, SCM_RIGHTS, SOL_SOCKET};
|
||||
|
||||
// Socket ioctls used by gVisor unix socket tests.
|
||||
const TIOCOUTQ: u32 = 0x5411; // Get output queue size
|
||||
const FIONREAD: u32 = 0x541B; // Get input queue size (aka TIOCINQ)
|
||||
const SIOCGIFINDEX: u32 = 0x8933; // name -> if_index mapping
|
||||
|
||||
fn clamp_usize_to_i32(v: usize) -> i32 {
|
||||
core::cmp::min(v, i32::MAX as usize) as i32
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
struct Linger {
|
||||
|
|
@ -516,41 +507,12 @@ impl Socket for UnixStreamSocket {
|
|||
&self.open_files
|
||||
}
|
||||
|
||||
fn ioctl(
|
||||
&self,
|
||||
cmd: u32,
|
||||
arg: usize,
|
||||
_private_data: &FilePrivateData,
|
||||
) -> Result<usize, SystemError> {
|
||||
if arg == 0 {
|
||||
return Err(SystemError::EFAULT);
|
||||
}
|
||||
fn recv_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Ok(self.ioctl_fionread())
|
||||
}
|
||||
|
||||
match cmd {
|
||||
// Return bytes available for reading.
|
||||
FIONREAD => {
|
||||
let available = self.ioctl_fionread();
|
||||
let mut writer =
|
||||
UserBufferWriter::new(arg as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
writer
|
||||
.buffer_protected(0)?
|
||||
.write_one::<i32>(0, &clamp_usize_to_i32(available))?;
|
||||
Ok(0)
|
||||
}
|
||||
// Return bytes queued for transmission.
|
||||
TIOCOUTQ => {
|
||||
let queued = self.ioctl_tiocoutq();
|
||||
let mut writer =
|
||||
UserBufferWriter::new(arg as *mut u8, core::mem::size_of::<i32>(), true)?;
|
||||
writer
|
||||
.buffer_protected(0)?
|
||||
.write_one::<i32>(0, &clamp_usize_to_i32(queued))?;
|
||||
Ok(0)
|
||||
}
|
||||
// Netdevice ioctls on AF_UNIX sockets: gVisor tests accept ENODEV.
|
||||
SIOCGIFINDEX => Err(SystemError::ENODEV),
|
||||
_ => Err(SystemError::ENOSYS),
|
||||
}
|
||||
fn send_bytes_available(&self) -> Result<usize, SystemError> {
|
||||
Ok(self.ioctl_tiocoutq())
|
||||
}
|
||||
|
||||
fn set_nonblocking(&self, nonblocking: bool) {
|
||||
|
|
|
|||
|
|
@ -163,18 +163,16 @@ pub(super) fn do_recvfrom(
|
|||
pmsg_flags.insert(socket::PMSG::DONTWAIT);
|
||||
}
|
||||
|
||||
if addr.is_null() {
|
||||
let (n, _) = socket.recv_from(buf, pmsg_flags, None)?;
|
||||
return Ok(n);
|
||||
}
|
||||
|
||||
// Linux 语义:recvfrom 的 addr/addrlen 是纯输出参数,内核不得读取 addr 缓冲区内容。
|
||||
// 用户栈上的 sockaddr 可能是未初始化的;读取它会导致错误解析并返回 EINVAL。
|
||||
if addr_len.is_null() {
|
||||
return Err(SystemError::EFAULT);
|
||||
}
|
||||
// recv() passes NULL for both addr and addr_len, which is valid.
|
||||
|
||||
let (recv_len, endpoint) = socket.recv_from(buf, pmsg_flags, None)?;
|
||||
endpoint.write_to_user(addr, addr_len)?;
|
||||
|
||||
// Only write the source address if the caller provided addr and addr_len
|
||||
if !addr.is_null() && !addr_len.is_null() {
|
||||
endpoint.write_to_user(addr, addr_len)?;
|
||||
}
|
||||
|
||||
Ok(recv_len)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,14 @@ pub(super) fn do_recvmsg(
|
|||
let reader = UserBufferReader::new(msg, core::mem::size_of::<MsgHdr>(), from_user)?;
|
||||
let mut kmsg = reader.buffer_protected(0)?.read_one::<MsgHdr>(0)?;
|
||||
|
||||
// log::debug!(
|
||||
// "do_recvmsg: fd={}, msg_iovlen={}, msg_iov={:?}, flags={:#x}",
|
||||
// fd,
|
||||
// kmsg.msg_iovlen,
|
||||
// kmsg.msg_iov,
|
||||
// flags
|
||||
// );
|
||||
|
||||
// 检查每个缓冲区地址是否合法,生成iovecs(fallback path needs this).
|
||||
let iovs = unsafe { IoVecs::from_user(kmsg.msg_iov, kmsg.msg_iovlen, true)? };
|
||||
|
||||
|
|
|
|||
|
|
@ -490,14 +490,20 @@ impl UserBufferReader<'_> {
|
|||
}
|
||||
|
||||
fn convert_with_offset<T>(&self, src: &[u8], offset: usize) -> Result<&[T], SystemError> {
|
||||
if offset >= src.len() {
|
||||
// offset == src.len is valid, as long as don't try to dereference it in &src[offset..]
|
||||
if offset > src.len() {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let byte_buffer: &[u8] = &src[offset..];
|
||||
if !byte_buffer.len().is_multiple_of(core::mem::size_of::<T>()) || byte_buffer.is_empty() {
|
||||
if byte_buffer.is_empty() {
|
||||
// Empty buffer is valid - return empty slice
|
||||
return Ok(&[]);
|
||||
}
|
||||
if !byte_buffer.len().is_multiple_of(core::mem::size_of::<T>()) {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
debug_assert!(offset < src.len());
|
||||
let chunks = unsafe {
|
||||
from_raw_parts(
|
||||
byte_buffer.as_ptr() as *const T,
|
||||
|
|
@ -776,15 +782,17 @@ impl<'a> UserBufferWriter<'a> {
|
|||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
let byte_buffer: &mut [u8] = &mut src[offset..];
|
||||
if !byte_buffer.len().is_multiple_of(core::mem::size_of::<T>()) {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
let len = byte_buffer.len() / core::mem::size_of::<T>();
|
||||
if len == 0 {
|
||||
// Empty buffer is valid - return empty slice
|
||||
return Ok(&mut []);
|
||||
}
|
||||
|
||||
if !byte_buffer.len().is_multiple_of(core::mem::size_of::<T>()) {
|
||||
return Err(SystemError::EINVAL);
|
||||
}
|
||||
|
||||
let chunks = unsafe { from_raw_parts_mut(byte_buffer.as_mut_ptr() as *mut T, len) };
|
||||
return Ok(chunks);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +1,15 @@
|
|||
**/1
|
||||
**/2
|
||||
UdpInet6SocketTest.ConnectInet4Sockaddr
|
||||
# **.Creation
|
||||
# **.Getsockname
|
||||
# **.Getpeername
|
||||
**.SendNotConnected/**
|
||||
**.ConnectBinds/**
|
||||
**.ReceiveNotBound/**
|
||||
**.Bind/**
|
||||
**.BindInUse/**
|
||||
**.ConnectWriteToInvalidPort/**
|
||||
**.ConnectSimultaneousWriteToInvalidPort/**
|
||||
**.ReceiveAfterConnect/**
|
||||
**.ReceiveAfterDisconnect/**
|
||||
**.Connect/**
|
||||
**.ConnectAnyZero/**
|
||||
**.ConnectAnyWithPort/**
|
||||
**.DisconnectAfterConnectAny/**
|
||||
**.DisconnectAfterConnectAnyWithPort/**
|
||||
**.DisconnectAfterBind/**
|
||||
**.DisconnectAfterBindToUnspecAndConnect/**
|
||||
**.DisconnectAfterConnectWithoutBind/**
|
||||
**.BindToAnyConnnectToLocalhost/**
|
||||
**.DisconnectAfterBindToAny/**
|
||||
**.Disconnect/**
|
||||
**.ConnectBadAddress/**
|
||||
**.SendToAddressOtherThanConnected/**
|
||||
**.ConnectAndSendNoReceiver/**
|
||||
**.RecvErrorConnRefusedOtherAFSockOpt/**
|
||||
**.RecvErrorConnRefused/**
|
||||
**.ZerolengthWriteAllowed/**
|
||||
**.ZerolengthWriteAllowedNonBlockRead/**
|
||||
**.SendAndReceiveNotConnected/**
|
||||
**.SendAndReceiveConnected/**
|
||||
**.ReceiveFromNotConnected/**
|
||||
**.ReceiveBeforeConnect/**
|
||||
**.ReceiveFrom/**
|
||||
**.Listen/**
|
||||
**.Accept/**
|
||||
**.ReadShutdownNonblockPendingData/**
|
||||
**.ReadShutdownSameSocketResetsShutdownState/**
|
||||
**.ReadShutdown/**
|
||||
**.ReadShutdownDifferentThread/**
|
||||
**.WriteShutdown/**
|
||||
**.SynchronousReceive/**
|
||||
**.BoundaryPreserved_SendRecv/**
|
||||
**.BoundaryPreserved_WritevReadv/**
|
||||
**.BoundaryPreserved_SendMsgRecvMsg/**
|
||||
**.FIONREADShutdown/**
|
||||
**.FIONREADWriteShutdown/**
|
||||
**.Fionread/**
|
||||
**.FIONREADZeroLengthPacket/**
|
||||
**.FIONREADZeroLengthWriteShutdown/**
|
||||
**.SoNoCheckOffByDefault/**
|
||||
**.SoNoCheck/**
|
||||
**.ErrorQueue/**
|
||||
**.SoTimestampOffByDefault/**
|
||||
**.SoTimestamp/**
|
||||
**.WriteShutdownNotConnected/**
|
||||
**.TimestampIoctl/**
|
||||
**.TimestampIoctlNothingRead/**
|
||||
**.TimestampIoctlPersistence/**
|
||||
**.RecvBufLimitsEmptyRcvBuf/**
|
||||
**.RecvBufLimits/**
|
||||
**.SetSocketDetachFilter/**
|
||||
**.SetSocketDetachFilterNoInstalledFilter/**
|
||||
**.GetSocketDetachFilter/**
|
||||
**.SendToZeroPort/**
|
||||
**.ConnectToZeroPortUnbound/**
|
||||
**.ConnectToZeroPortBound/**
|
||||
**.ConnectToZeroPortConnected/**
|
||||
**.SetAndReceiveTOSOrTClass/**
|
||||
**.SendAndReceiveTOSorTClass/**
|
||||
**.SetAndReceiveTTLOrHopLimit/**
|
||||
**.SendAndReceiveTTLOrHopLimit/**
|
||||
**.SetAndReceivePktInfo/**
|
||||
**.SendPacketLargerThanSendBufOnNonBlockingSocket/**
|
||||
**.ReadShutdownOnBoundSocket/**
|
||||
**.ReconnectDoesNotClearReadShutdown/**
|
||||
**.ReconnectDoesNotClearWriteShutdown/**
|
||||
**SoTimestamp**
|
||||
**TimestampIoctl**
|
||||
**RecvBufLimits**
|
||||
**UdpSocketControlMessagesTest**
|
||||
**SetSocketDetachFilter**
|
||||
# ICMP
|
||||
**ConnectWriteToInvalidPort**
|
||||
**ConnectAndSendNoReceiver**
|
||||
# Message Queue
|
||||
**RecvErrorConnRefused**
|
||||
# AF Option Isolation
|
||||
**RecvErrorRefusedOtherAFSockOpt**
|
||||
|
|
|
|||
Loading…
Reference in New Issue