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:
Samuel Dai 2026-01-01 13:23:31 +08:00 committed by GitHub
parent 84368a9419
commit a04c6c1ca6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1240 additions and 529 deletions

View File

@ -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(),
})

View File

@ -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 {

View File

@ -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);

View File

@ -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 {

View File

@ -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

View File

@ -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),
}

View File

@ -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()?),

View File

@ -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.

View File

@ -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

View File

@ -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 ...
}
}

View File

@ -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()
}
}

View File

@ -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 {

View File

@ -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()

View File

@ -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)
}
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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)
}

View File

@ -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
// );
// 检查每个缓冲区地址是否合法生成iovecsfallback path needs this).
let iovs = unsafe { IoVecs::from_user(kmsg.msg_iov, kmsg.msg_iovlen, true)? };

View File

@ -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);
}

View File

@ -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**