use std::collections::{BTreeMap, VecDeque};
use crate::abi::errno::Errno;
use crate::vcpu::GuestMemory;
use super::{Fd, Kernel, err};
impl Errno {
const ENOTCONN: Errno = Errno(107);
const EPROTONOSUPPORT: Errno = Errno(93);
}
const AF_UNIX: u16 = 1;
const AF_INET: u16 = 2;
const AF_INET6: u16 = 10;
const AF_NETLINK: u16 = 16;
const SOCK_STREAM: u64 = 1;
const SOCK_DGRAM: u64 = 2;
const SOCK_RAW: u64 = 3;
const SOCK_NONBLOCK: u64 = 0o4000;
const NETLINK_ROUTE: u64 = 0;
const SOL_SOCKET: u64 = 1;
const IPPROTO_IP: u64 = 0;
const IPPROTO_TCP: u64 = 6;
const IPPROTO_IPV6: u64 = 41;
const SO_REUSEADDR: u64 = 2;
const SO_TYPE: u64 = 3;
const SO_ERROR: u64 = 4;
const SO_BROADCAST: u64 = 6;
const SO_SNDBUF: u64 = 7;
const SO_RCVBUF: u64 = 8;
const SO_KEEPALIVE: u64 = 9;
const SO_LINGER: u64 = 13;
const SO_REUSEPORT: u64 = 15;
const SO_RCVTIMEO: u64 = 20;
const SO_SNDTIMEO: u64 = 21;
const SO_ACCEPTCONN: u64 = 30;
const SO_PROTOCOL: u64 = 38;
const SO_DOMAIN: u64 = 39;
const IP_TOS: u64 = 1;
const TCP_NODELAY: u64 = 1;
const IPV6_V6ONLY: u64 = 26;
const MSG_PEEK: u64 = 0x02;
const MSG_TRUNC: u64 = 0x20;
const MSG_DONTWAIT: u64 = 0x40;
const MSG_WAITALL: u64 = 0x100;
const RTM_NEWLINK: u16 = 16;
const RTM_GETLINK: u16 = 18;
const RTM_NEWADDR: u16 = 20;
const RTM_GETADDR: u16 = 22;
const RTM_GETROUTE: u16 = 26;
const NLMSG_ERROR: u16 = 2;
const NLMSG_DONE: u16 = 3;
const NLM_F_ACK: u16 = 0x04;
const NLM_F_DUMP: u16 = 0x100 | 0x200;
const ARPHRD_LOOPBACK: u16 = 772;
const IFF_UP: u32 = 0x1;
const IFF_LOOPBACK: u32 = 0x8;
const IFF_RUNNING: u32 = 0x40;
const IFLA_ADDRESS: u16 = 1;
const IFLA_IFNAME: u16 = 3;
const IFLA_MTU: u16 = 4;
const IFA_ADDRESS: u16 = 1;
const IFA_LOCAL: u16 = 2;
const IFA_LABEL: u16 = 3;
const RT_SCOPE_HOST: u8 = 254;
const LOOPBACK_IFINDEX: i32 = 1;
#[derive(Debug, Default)]
pub(super) struct Net {
socks: Vec<Sock>,
listeners: BTreeMap<String, usize>,
dgram_ports: BTreeMap<String, usize>,
}
#[derive(Debug)]
struct Sock {
domain: u16,
kind: Kind,
nonblock: bool,
opts: SockOpts,
}
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug)]
struct SockOpts {
reuseaddr: bool,
reuseport: bool,
keepalive: bool,
broadcast: bool,
nodelay: bool,
v6only: bool,
linger_on: bool,
linger_secs: i32,
rcvtimeo: [u8; 16],
sndtimeo: [u8; 16],
rcvbuf: u32,
sndbuf: u32,
tos: u32,
error: i32,
}
impl Default for SockOpts {
fn default() -> Self {
Self {
reuseaddr: false,
reuseport: false,
keepalive: false,
broadcast: false,
nodelay: false,
v6only: false,
linger_on: false,
linger_secs: 0,
rcvtimeo: [0; 16],
sndtimeo: [0; 16],
rcvbuf: 212_992,
sndbuf: 212_992,
tos: 0,
error: 0,
}
}
}
#[derive(Debug)]
enum Kind {
Idle { bound: Option<Addr> },
Listener {
addr: Option<Addr>,
backlog: VecDeque<usize>,
},
Pair(Pair),
Dgram(Dgram),
Netlink(Netlink),
}
#[derive(Debug)]
struct Pair {
to: [VecDeque<u8>; 2],
refs: [usize; 2],
shut_wr: [bool; 2],
shut_rd: [bool; 2],
nonblock: [bool; 2],
addrs: [Option<InetAddr>; 2],
}
impl Pair {
fn new() -> Self {
Self {
to: [VecDeque::new(), VecDeque::new()],
refs: [1, 1],
shut_wr: [false, false],
shut_rd: [false, false],
nonblock: [false, false],
addrs: [None, None],
}
}
}
#[derive(Debug, Default)]
struct Dgram {
local: Option<InetAddr>,
peer: Option<InetAddr>,
queue: VecDeque<(InetAddr, Vec<u8>)>,
}
#[derive(Debug, Default)]
struct Netlink {
pid: u32,
sotype: u64,
queue: VecDeque<Vec<u8>>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
enum Addr {
Unix(String),
Inet(InetAddr),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct InetAddr {
v6: bool,
port: u16,
ip: [u8; 16],
}
impl InetAddr {
fn is_any(&self) -> bool {
self.ip == [0u8; 16]
}
fn is_loopback(&self) -> bool {
if self.v6 {
self.ip == loopback_ip(true)
} else {
self.ip[0] == 127
}
}
fn valid_bind(&self) -> bool {
self.is_any() || self.is_loopback()
}
}
fn loopback_ip(v6: bool) -> [u8; 16] {
let mut ip = [0u8; 16];
if v6 {
ip[15] = 1;
} else {
ip[0] = 127;
ip[3] = 1;
}
ip
}
fn route_key(proto: &str, a: InetAddr) -> String {
format!("{proto}{}:{}", if a.v6 { 6 } else { 4 }, a.port)
}
impl Net {
pub(super) fn bump(&mut self, fd: &Fd, inc: bool) {
if let Fd::Socket { sock, end } = *fd
&& let Some(Kind::Pair(p)) = self.socks.get_mut(sock).map(|s| &mut s.kind)
{
if inc {
p.refs[end] += 1;
} else {
p.refs[end] = p.refs[end].saturating_sub(1);
}
}
}
fn addr_in_use(&self, proto: &str, a: InetAddr, exclude: usize) -> bool {
let key = route_key(proto, a);
self.socks.iter().enumerate().any(|(i, s)| {
if i == exclude {
return false;
}
match &s.kind {
Kind::Idle {
bound: Some(Addr::Inet(b)),
} if proto == "tcp" => route_key("tcp", *b) == key,
Kind::Listener {
addr: Some(Addr::Inet(b)),
..
} if proto == "tcp" => route_key("tcp", *b) == key,
Kind::Dgram(d) if proto == "udp" => {
d.local.is_some_and(|b| route_key("udp", b) == key)
}
_ => false,
}
})
}
fn ephemeral_port(&self, proto: &str, v6: bool) -> u16 {
for port in 32_768u32..=60_999 {
let a = InetAddr {
v6,
port: port as u16,
ip: [0; 16],
};
if !self.addr_in_use(proto, a, usize::MAX) {
return port as u16;
}
}
0
}
fn fresh_local(&self, v6: bool) -> InetAddr {
InetAddr {
v6,
port: self.ephemeral_port("tcp", v6),
ip: loopback_ip(v6),
}
}
}
impl Kernel {
fn sock_of(&self, fd: u64) -> Option<(usize, usize)> {
match self.cur.fds.get(fd as i32) {
Some(Fd::Socket { sock, end }) => Some((*sock, *end)),
_ => None,
}
}
fn ensure_dgram_bound(&mut self, sock: usize) -> InetAddr {
if let Kind::Dgram(d) = &self.net.socks[sock].kind
&& let Some(local) = d.local
{
return local;
}
let v6 = self.net.socks[sock].domain == AF_INET6;
let port = self.net.ephemeral_port("udp", v6);
let local = InetAddr {
v6,
port,
ip: loopback_ip(v6),
};
if let Kind::Dgram(d) = &mut self.net.socks[sock].kind {
d.local = Some(local);
}
self.net.dgram_ports.insert(route_key("udp", local), sock);
local
}
pub(super) fn sys_socket(&mut self, domain: u64, sotype: u64, protocol: u64) -> i64 {
let domain = domain as u16;
if domain == AF_NETLINK {
let base_type = sotype & 0xf;
if base_type != SOCK_RAW && base_type != SOCK_DGRAM {
return err(Errno::EOPNOTSUPP);
}
if protocol != NETLINK_ROUTE {
return err(Errno::EPROTONOSUPPORT);
}
let nonblock = sotype & SOCK_NONBLOCK != 0;
let kind = Kind::Netlink(Netlink {
sotype: base_type,
..Netlink::default()
});
let idx = self.net.socks.len();
self.net.socks.push(Sock {
domain,
kind,
nonblock,
opts: SockOpts::default(),
});
return i64::from(self.cur.fds.alloc(Fd::Socket { sock: idx, end: 0 }));
}
if domain != AF_UNIX && domain != AF_INET && domain != AF_INET6 {
return err(Errno::EAFNOSUPPORT);
}
let base_type = sotype & 0xf;
if base_type != SOCK_STREAM && !(domain != AF_UNIX && base_type == SOCK_DGRAM) {
return err(Errno::EOPNOTSUPP);
}
let nonblock = sotype & SOCK_NONBLOCK != 0;
let kind = if base_type == SOCK_DGRAM {
Kind::Dgram(Dgram::default())
} else {
Kind::Idle { bound: None }
};
let idx = self.net.socks.len();
self.net.socks.push(Sock {
domain,
kind,
nonblock,
opts: SockOpts::default(),
});
i64::from(self.cur.fds.alloc(Fd::Socket { sock: idx, end: 0 }))
}
pub(super) fn sys_socketpair(
&mut self,
domain: u64,
sotype: u64,
_protocol: u64,
sv: u64,
mem: &mut GuestMemory,
) -> i64 {
if domain as u16 != AF_UNIX {
return err(Errno::EAFNOSUPPORT);
}
if sotype & 0xf != SOCK_STREAM {
return err(Errno::EOPNOTSUPP);
}
let nonblock = sotype & SOCK_NONBLOCK != 0;
let mut pair = Pair::new();
pair.nonblock = [nonblock, nonblock];
let idx = self.net.socks.len();
self.net.socks.push(Sock {
domain: AF_UNIX,
kind: Kind::Pair(pair),
nonblock,
opts: SockOpts::default(),
});
let fd0 = self.cur.fds.alloc(Fd::Socket { sock: idx, end: 0 });
let fd1 = self.cur.fds.alloc(Fd::Socket { sock: idx, end: 1 });
let mut b = [0u8; 8];
b[0..4].copy_from_slice(&fd0.to_le_bytes());
b[4..8].copy_from_slice(&fd1.to_le_bytes());
if mem.write(sv, &b).is_err() {
return err(Errno::EFAULT);
}
0
}
pub(super) fn sys_bind(&mut self, fd: u64, addr: u64, addrlen: u64, mem: &GuestMemory) -> i64 {
let Some((sock, _)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
if self.net.socks[sock].domain == AF_NETLINK {
return self.bind_netlink(sock, addr, addrlen, mem);
}
let Some(parsed) = read_sockaddr(mem, addr, addrlen) else {
return err(Errno::EINVAL);
};
match parsed {
Addr::Unix(_) => {
if self.net.socks[sock].domain != AF_UNIX {
return err(Errno::EINVAL);
}
match &mut self.net.socks[sock].kind {
Kind::Idle { bound } => {
*bound = Some(parsed);
0
}
_ => err(Errno::EINVAL),
}
}
Addr::Inet(mut a) => {
let domain = self.net.socks[sock].domain;
if (a.v6 && domain != AF_INET6) || (!a.v6 && domain != AF_INET) {
return err(Errno::EINVAL);
}
if !a.valid_bind() {
return err(Errno::EINVAL); }
let proto = match &self.net.socks[sock].kind {
Kind::Idle { .. } => "tcp",
Kind::Dgram(_) => "udp",
_ => return err(Errno::EINVAL),
};
if a.port == 0 {
a.port = self.net.ephemeral_port(proto, a.v6);
} else if !self.net.socks[sock].opts.reuseaddr
&& self.net.addr_in_use(proto, a, sock)
{
return err(Errno::EINVAL); }
match &mut self.net.socks[sock].kind {
Kind::Idle { bound } => *bound = Some(Addr::Inet(a)),
Kind::Dgram(d) => d.local = Some(a),
_ => return err(Errno::EINVAL),
}
if proto == "udp" {
self.net.dgram_ports.insert(route_key("udp", a), sock);
}
0
}
}
}
pub(super) fn sys_listen(&mut self, fd: u64) -> i64 {
let Some((sock, _)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let domain = self.net.socks[sock].domain;
let mut bound = match &self.net.socks[sock].kind {
Kind::Idle { bound } => bound.clone(),
Kind::Listener { .. } => return 0,
_ => return err(Errno::EINVAL),
};
if bound.is_none() && domain != AF_UNIX {
let v6 = domain == AF_INET6;
let port = self.net.ephemeral_port("tcp", v6);
bound = Some(Addr::Inet(InetAddr {
v6,
port,
ip: [0; 16],
}));
}
let key = match &bound {
Some(Addr::Unix(p)) => Some(p.clone()),
Some(Addr::Inet(a)) => Some(route_key("tcp", *a)),
None => None,
};
self.net.socks[sock].kind = Kind::Listener {
addr: bound,
backlog: VecDeque::new(),
};
if let Some(k) = key {
self.net.listeners.insert(k, sock);
}
0
}
pub(super) fn sys_connect(
&mut self,
fd: u64,
addr: u64,
addrlen: u64,
mem: &GuestMemory,
) -> i64 {
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let Some(target) = read_sockaddr(mem, addr, addrlen) else {
return err(Errno::EINVAL);
};
let domain = self.net.socks[sock].domain;
let mismatched = match (&target, domain) {
(Addr::Unix(_), AF_UNIX) => false,
(Addr::Inet(a), AF_INET) => a.v6,
(Addr::Inet(a), AF_INET6) => !a.v6,
_ => true,
};
if mismatched {
return err(Errno::EINVAL);
}
if let Addr::Inet(a) = &target
&& !a.valid_bind()
{
return err(Errno::ECONNREFUSED); }
if matches!(self.net.socks[sock].kind, Kind::Dgram(_)) {
let Addr::Inet(peer) = target else {
unreachable!("validated above")
};
self.ensure_dgram_bound(sock);
if let Kind::Dgram(d) = &mut self.net.socks[sock].kind {
d.peer = Some(peer);
}
return 0;
}
if !matches!(&self.net.socks[sock].kind, Kind::Idle { .. } if end == 0) {
return err(Errno::EINVAL);
}
let key = match &target {
Addr::Unix(p) => p.clone(),
Addr::Inet(a) => route_key("tcp", *a),
};
let Some(&lidx) = self.net.listeners.get(&key) else {
return err(Errno::ECONNREFUSED);
};
let listener_addr = match &self.net.socks[lidx].kind {
Kind::Listener { addr, .. } => addr.clone(),
_ => return err(Errno::ECONNREFUSED),
};
let mut pair = Pair::new();
pair.nonblock[0] = self.net.socks[sock].nonblock;
if domain != AF_UNIX {
let v6 = domain == AF_INET6;
let mut peer_addr = match listener_addr {
Some(Addr::Inet(a)) => a,
_ => InetAddr {
v6,
port: 0,
ip: loopback_ip(v6),
},
};
if peer_addr.is_any() {
peer_addr.ip = loopback_ip(v6);
}
pair.addrs[0] = Some(self.net.fresh_local(v6));
pair.addrs[1] = Some(peer_addr);
}
self.net.socks[sock].kind = Kind::Pair(pair);
if let Kind::Listener { backlog, .. } = &mut self.net.socks[lidx].kind {
backlog.push_back(sock);
}
0
}
pub(super) fn sys_accept4(
&mut self,
fd: u64,
addr: u64,
addrlen: u64,
flags: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some((sock, _)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let nonblock = self.net.socks[sock].nonblock;
let pending = match &mut self.net.socks[sock].kind {
Kind::Listener { backlog, .. } => backlog.pop_front(),
_ => return err(Errno::EINVAL),
};
let Some(pidx) = pending else {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true; return 0;
};
let domain = self.net.socks[sock].domain;
let peer = match &self.net.socks[pidx].kind {
Kind::Pair(p) => p.addrs[0].map(Addr::Inet),
_ => None,
};
write_sockaddr(mem, addr, addrlen, domain, peer.as_ref());
if let Kind::Pair(p) = &mut self.net.socks[pidx].kind {
p.nonblock[1] = flags & SOCK_NONBLOCK != 0;
}
i64::from(self.cur.fds.alloc(Fd::Socket { sock: pidx, end: 1 }))
}
fn bind_netlink(&mut self, sock: usize, addr: u64, addrlen: u64, mem: &GuestMemory) -> i64 {
if addrlen < 8 {
return err(Errno::EINVAL);
}
let Ok(b) = mem.read_vec(addr, 8) else {
return err(Errno::EFAULT);
};
if u16::from_le_bytes([b[0], b[1]]) != AF_NETLINK {
return err(Errno::EINVAL);
}
let requested = u32::from_le_bytes([b[4], b[5], b[6], b[7]]);
let pid = if requested == 0 {
sock as u32 + 1
} else {
requested
};
let Kind::Netlink(nl) = &mut self.net.socks[sock].kind else {
return err(Errno::EINVAL);
};
nl.pid = pid;
0
}
pub(super) fn sys_getsockname(
&mut self,
fd: u64,
addr: u64,
addrlen: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let domain = self.net.socks[sock].domain;
if domain == AF_NETLINK {
let pid = match &self.net.socks[sock].kind {
Kind::Netlink(nl) => nl.pid,
_ => 0,
};
return write_netlink_sockaddr(mem, addr, addrlen, pid);
}
let resolved = match &self.net.socks[sock].kind {
Kind::Idle { bound } => bound.clone(),
Kind::Listener { addr, .. } => addr.clone(),
Kind::Pair(p) => p.addrs[end].map(Addr::Inet),
Kind::Dgram(d) => d.local.map(Addr::Inet),
Kind::Netlink(_) => None,
};
write_sockaddr(mem, addr, addrlen, domain, resolved.as_ref())
}
pub(super) fn sys_getpeername(
&mut self,
fd: u64,
addr: u64,
addrlen: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let domain = self.net.socks[sock].domain;
match &self.net.socks[sock].kind {
Kind::Pair(p) => write_sockaddr(
mem,
addr,
addrlen,
domain,
p.addrs[1 - end].map(Addr::Inet).as_ref(),
),
Kind::Dgram(d) if d.peer.is_some() => {
write_sockaddr(mem, addr, addrlen, domain, d.peer.map(Addr::Inet).as_ref())
}
_ => err(Errno::ENOTCONN),
}
}
pub(super) fn sys_shutdown(&mut self, fd: u64, how: u64) -> i64 {
const SHUT_RD: u64 = 0;
const SHUT_WR: u64 = 1;
const SHUT_RDWR: u64 = 2;
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
match &mut self.net.socks[sock].kind {
Kind::Pair(p) => {
match how {
SHUT_RD => p.shut_rd[end] = true,
SHUT_WR => p.shut_wr[end] = true,
SHUT_RDWR => {
p.shut_rd[end] = true;
p.shut_wr[end] = true;
}
_ => return err(Errno::EINVAL),
}
0
}
_ => err(Errno::ENOTCONN),
}
}
#[allow(clippy::too_many_arguments)]
pub(super) fn sys_setsockopt(
&mut self,
fd: u64,
level: u64,
optname: u64,
optval: u64,
optlen: u64,
mem: &GuestMemory,
) -> i64 {
let Some((sock, _)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
let opts = &mut self.net.socks[sock].opts;
if level == SOL_SOCKET {
match optname {
SO_REUSEADDR if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.reuseaddr = v != 0;
}
}
SO_REUSEPORT if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.reuseport = v != 0;
}
}
SO_KEEPALIVE if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.keepalive = v != 0;
}
}
SO_BROADCAST if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.broadcast = v != 0;
}
}
SO_RCVBUF if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.rcvbuf = v;
}
}
SO_SNDBUF if optlen >= 4 => {
if let Ok(v) = mem.read_u32(optval) {
opts.sndbuf = v;
}
}
SO_LINGER if optlen >= 8 => {
if let Ok(b) = mem.read_vec(optval, 8) {
opts.linger_on = i32::from_le_bytes([b[0], b[1], b[2], b[3]]) != 0;
opts.linger_secs = i32::from_le_bytes([b[4], b[5], b[6], b[7]]);
}
}
SO_RCVTIMEO if optlen >= 16 => {
if let Ok(b) = mem.read_vec(optval, 16) {
opts.rcvtimeo.copy_from_slice(&b);
}
}
SO_SNDTIMEO if optlen >= 16 => {
if let Ok(b) = mem.read_vec(optval, 16) {
opts.sndtimeo.copy_from_slice(&b);
}
}
_ => {}
}
} else if level == IPPROTO_TCP && optname == TCP_NODELAY && optlen >= 4 {
if let Ok(v) = mem.read_u32(optval) {
opts.nodelay = v != 0;
}
} else if level == IPPROTO_IPV6 && optname == IPV6_V6ONLY && optlen >= 4 {
if let Ok(v) = mem.read_u32(optval) {
opts.v6only = v != 0;
}
} else if level == IPPROTO_IP
&& optname == IP_TOS
&& optlen >= 4
&& let Ok(v) = mem.read_u32(optval)
{
opts.tos = v;
}
0
}
#[allow(clippy::too_many_arguments, clippy::too_many_lines)]
pub(super) fn sys_getsockopt(
&mut self,
fd: u64,
level: u64,
optname: u64,
optval: u64,
optlen_ptr: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some((sock, _)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
if level == SOL_SOCKET && optname == SO_LINGER {
let opts = &self.net.socks[sock].opts;
let mut b = [0u8; 8];
b[0..4].copy_from_slice(&i32::from(opts.linger_on).to_le_bytes());
b[4..8].copy_from_slice(&opts.linger_secs.to_le_bytes());
return write_optval(mem, optval, optlen_ptr, &b);
}
if level == SOL_SOCKET && (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
let opts = &self.net.socks[sock].opts;
let b = if optname == SO_RCVTIMEO {
opts.rcvtimeo
} else {
opts.sndtimeo
};
return write_optval(mem, optval, optlen_ptr, &b);
}
let value: u32 = if level == SOL_SOCKET {
match optname {
SO_TYPE => match &self.net.socks[sock].kind {
Kind::Dgram(_) => SOCK_DGRAM as u32,
Kind::Netlink(nl) => nl.sotype as u32,
_ => SOCK_STREAM as u32,
},
SO_ERROR => {
let e = self.net.socks[sock].opts.error;
self.net.socks[sock].opts.error = 0; e as u32
}
SO_REUSEADDR => u32::from(self.net.socks[sock].opts.reuseaddr),
SO_REUSEPORT => u32::from(self.net.socks[sock].opts.reuseport),
SO_KEEPALIVE => u32::from(self.net.socks[sock].opts.keepalive),
SO_BROADCAST => u32::from(self.net.socks[sock].opts.broadcast),
SO_RCVBUF => self.net.socks[sock].opts.rcvbuf,
SO_SNDBUF => self.net.socks[sock].opts.sndbuf,
SO_ACCEPTCONN => {
u32::from(matches!(self.net.socks[sock].kind, Kind::Listener { .. }))
}
SO_DOMAIN => u32::from(self.net.socks[sock].domain),
SO_PROTOCOL => match (&self.net.socks[sock].kind, self.net.socks[sock].domain) {
(Kind::Dgram(_), _) => 17, (_, d) if d == AF_UNIX || d == AF_NETLINK => 0, _ => 6, },
_ => 0,
}
} else if level == IPPROTO_TCP && optname == TCP_NODELAY {
u32::from(self.net.socks[sock].opts.nodelay)
} else if level == IPPROTO_IPV6 && optname == IPV6_V6ONLY {
u32::from(self.net.socks[sock].opts.v6only)
} else if level == IPPROTO_IP && optname == IP_TOS {
self.net.socks[sock].opts.tos
} else {
0
};
write_optval(mem, optval, optlen_ptr, &value.to_le_bytes())
}
#[allow(clippy::too_many_arguments)]
pub(super) fn sys_sendto(
&mut self,
fd: u64,
buf: u64,
len: u64,
_flags: u64,
dest_addr: u64,
dest_addrlen: u64,
mem: &GuestMemory,
) -> i64 {
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
if dest_addr == 0 || self.net.socks[sock].domain == AF_NETLINK {
let Ok(data) = mem.read_vec(buf, len as usize) else {
return err(Errno::EFAULT);
};
return self.write_socket(sock, end, &data);
}
let Some(Addr::Inet(dest)) = read_sockaddr(mem, dest_addr, dest_addrlen) else {
return err(Errno::EINVAL);
};
if !matches!(self.net.socks[sock].kind, Kind::Dgram(_)) {
return err(Errno::EINVAL); }
if !dest.valid_bind() {
return err(Errno::EINVAL); }
let Ok(data) = mem.read_vec(buf, len as usize) else {
return err(Errno::EFAULT);
};
let src = self.ensure_dgram_bound(sock);
let key = route_key("udp", dest);
if let Some(&tgt) = self.net.dgram_ports.get(&key)
&& let Kind::Dgram(td) = &mut self.net.socks[tgt].kind
{
td.queue.push_back((src, data.clone()));
}
data.len() as i64
}
#[allow(clippy::too_many_arguments)]
pub(super) fn sys_recvfrom(
&mut self,
fd: u64,
buf: u64,
len: u64,
flags: u64,
src_addr: u64,
src_addrlen: u64,
mem: &mut GuestMemory,
) -> i64 {
let Some((sock, end)) = self.sock_of(fd) else {
return err(Errno::ENOTSOCK);
};
if matches!(self.net.socks[sock].kind, Kind::Netlink(_)) {
let nonblock = self.net.socks[sock].nonblock || flags & MSG_DONTWAIT != 0;
let Some(data) = self.drain_netlink(sock, len as usize) else {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
};
write_netlink_sockaddr(mem, src_addr, src_addrlen, 0);
let n = data.len();
if mem.write(buf, &data).is_err() {
return err(Errno::EFAULT);
}
return n as i64;
}
if !matches!(self.net.socks[sock].kind, Kind::Dgram(_)) {
let n = self.recv_stream(sock, end, buf, len, mem, flags);
if n > 0 {
let domain = self.net.socks[sock].domain;
let peer = match &self.net.socks[sock].kind {
Kind::Pair(p) => p.addrs[1 - end].map(Addr::Inet),
_ => None,
};
write_sockaddr(mem, src_addr, src_addrlen, domain, peer.as_ref());
}
return n;
}
let nonblock = self.net.socks[sock].nonblock || flags & MSG_DONTWAIT != 0;
let Some((from, data)) = self.recv_dgram_msg(sock, flags) else {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
};
let domain = self.net.socks[sock].domain;
write_sockaddr(mem, src_addr, src_addrlen, domain, Some(&Addr::Inet(from)));
let n = (len as usize).min(data.len());
if mem.write(buf, &data[..n]).is_err() {
return err(Errno::EFAULT);
}
if flags & MSG_TRUNC != 0 {
data.len() as i64
} else {
n as i64
}
}
fn recv_dgram_msg(&mut self, sock: usize, flags: u64) -> Option<(InetAddr, Vec<u8>)> {
let peek = flags & MSG_PEEK != 0;
match &mut self.net.socks[sock].kind {
Kind::Dgram(d) if peek => d.queue.front().cloned(),
Kind::Dgram(d) => d.queue.pop_front(),
_ => unreachable!("checked by caller"),
}
}
pub(super) fn read_socket(
&mut self,
sock: usize,
end: usize,
buf: u64,
count: u64,
mem: &mut GuestMemory,
) -> i64 {
if matches!(self.net.socks[sock].kind, Kind::Dgram(_)) {
let nonblock = self.net.socks[sock].nonblock;
let Some((_, data)) = self.recv_dgram_msg(sock, 0) else {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
};
let n = (count as usize).min(data.len());
if mem.write(buf, &data[..n]).is_err() {
return err(Errno::EFAULT);
}
return n as i64;
}
if matches!(self.net.socks[sock].kind, Kind::Netlink(_)) {
let nonblock = self.net.socks[sock].nonblock;
let Some(data) = self.drain_netlink(sock, count as usize) else {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
return 0;
};
if mem.write(buf, &data).is_err() {
return err(Errno::EFAULT);
}
return data.len() as i64;
}
self.recv_stream(sock, end, buf, count, mem, 0)
}
fn drain_netlink(&mut self, sock: usize, want: usize) -> Option<Vec<u8>> {
let Kind::Netlink(nl) = &mut self.net.socks[sock].kind else {
unreachable!("checked by caller")
};
if nl.queue.is_empty() {
return None;
}
let mut out = Vec::new();
while let Some(next) = nl.queue.front() {
if out.is_empty() && next.len() > want {
let msg = nl.queue.pop_front().expect("front just checked Some");
out.extend_from_slice(&msg[..want.min(msg.len())]);
break;
}
if out.len() + next.len() > want {
break;
}
let msg = nl.queue.pop_front().expect("front just checked Some");
out.extend_from_slice(&msg);
}
Some(out)
}
fn recv_stream(
&mut self,
sock: usize,
end: usize,
buf: u64,
count: u64,
mem: &mut GuestMemory,
flags: u64,
) -> i64 {
let (shut_rd, avail, peer_open, nonblock) = match &self.net.socks[sock].kind {
Kind::Pair(p) => (
p.shut_rd[end],
p.to[end].len(),
p.refs[1 - end] > 0 && !p.shut_wr[1 - end],
p.nonblock[end] || flags & MSG_DONTWAIT != 0,
),
_ => return err(Errno::EINVAL),
};
if shut_rd {
return 0;
}
let short = flags & MSG_WAITALL != 0
&& !nonblock
&& avail > 0
&& avail < count as usize
&& peer_open;
if avail == 0 || short {
if peer_open {
if nonblock {
return err(Errno::EAGAIN);
}
self.block = true;
}
return 0;
}
let peek = flags & MSG_PEEK != 0;
let data: Vec<u8> = match &mut self.net.socks[sock].kind {
Kind::Pair(p) => {
let n = (count as usize).min(p.to[end].len());
if peek {
p.to[end].iter().take(n).copied().collect()
} else {
p.to[end].drain(..n).collect()
}
}
_ => return err(Errno::EINVAL),
};
if mem.write(buf, &data).is_err() {
return err(Errno::EFAULT);
}
data.len() as i64
}
pub(super) fn write_socket(&mut self, sock: usize, end: usize, data: &[u8]) -> i64 {
if matches!(self.net.socks[sock].kind, Kind::Netlink(_)) {
return self.handle_netlink_request(sock, data);
}
if matches!(self.net.socks[sock].kind, Kind::Dgram(_)) {
let peer = match &self.net.socks[sock].kind {
Kind::Dgram(d) => d.peer,
_ => unreachable!("checked above"),
};
let Some(peer) = peer else {
return err(Errno::ENOTCONN);
};
let src = self.ensure_dgram_bound(sock);
let key = route_key("udp", peer);
if let Some(&tgt) = self.net.dgram_ports.get(&key)
&& let Kind::Dgram(td) = &mut self.net.socks[tgt].kind
{
td.queue.push_back((src, data.to_vec()));
}
return data.len() as i64;
}
match &mut self.net.socks[sock].kind {
Kind::Pair(p) => {
if p.shut_wr[end] || p.refs[1 - end] == 0 {
return err(Errno::EPIPE);
}
p.to[1 - end].extend(data.iter().copied());
data.len() as i64
}
_ => err(Errno::EINVAL),
}
}
fn handle_netlink_request(&mut self, sock: usize, data: &[u8]) -> i64 {
let pid = match &self.net.socks[sock].kind {
Kind::Netlink(nl) => nl.pid,
_ => 0,
};
let mut offset = 0usize;
while offset + 16 <= data.len() {
let hdr = &data[offset..offset + 16];
let nlmsg_len = u32::from_le_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]) as usize;
if nlmsg_len < 16 {
break; }
let nlmsg_type = u16::from_le_bytes([hdr[4], hdr[5]]);
let nlmsg_flags = u16::from_le_bytes([hdr[6], hdr[7]]);
let nlmsg_seq = u32::from_le_bytes([hdr[8], hdr[9], hdr[10], hdr[11]]);
let mut orig_hdr = [0u8; 16];
orig_hdr.copy_from_slice(hdr);
let want_ack = nlmsg_flags & NLM_F_ACK != 0;
let dump = nlmsg_flags & NLM_F_DUMP == NLM_F_DUMP;
let replies: Vec<Vec<u8>> = if nlmsg_type == RTM_GETLINK && dump {
vec![
build_rtm_newlink(nlmsg_seq, pid),
encode_nlmsg(NLMSG_DONE, 0, nlmsg_seq, pid, &0i32.to_le_bytes()),
]
} else if nlmsg_type == RTM_GETADDR && dump {
vec![
build_rtm_newaddr_v4(nlmsg_seq, pid),
build_rtm_newaddr_v6(nlmsg_seq, pid),
encode_nlmsg(NLMSG_DONE, 0, nlmsg_seq, pid, &0i32.to_le_bytes()),
]
} else if nlmsg_type == RTM_GETROUTE {
vec![encode_nlmsg(
NLMSG_DONE,
0,
nlmsg_seq,
pid,
&0i32.to_le_bytes(),
)]
} else if want_ack {
vec![encode_nlmsgerr(0, &orig_hdr, nlmsg_seq, pid)]
} else {
vec![encode_nlmsgerr(
-Errno::EOPNOTSUPP.0,
&orig_hdr,
nlmsg_seq,
pid,
)]
};
if let Kind::Netlink(nl) = &mut self.net.socks[sock].kind {
nl.queue.extend(replies);
}
offset += nlmsg_align(nlmsg_len);
}
data.len() as i64
}
}
fn read_sockaddr(mem: &GuestMemory, ptr: u64, addrlen: u64) -> Option<Addr> {
if addrlen < 2 {
return None;
}
let bytes = mem.read_vec(ptr, (addrlen as usize).min(128)).ok()?;
let family = u16::from_le_bytes([bytes[0], bytes[1]]);
match family {
AF_UNIX => {
let path = &bytes[2..];
if path.first() == Some(&0) {
let len = (addrlen as usize).saturating_sub(2).min(path.len());
Some(Addr::Unix(
String::from_utf8_lossy(&path[..len]).into_owned(),
))
} else {
let end = path.iter().position(|&c| c == 0).unwrap_or(path.len());
Some(Addr::Unix(
String::from_utf8_lossy(&path[..end]).into_owned(),
))
}
}
AF_INET if bytes.len() >= 8 => {
let port = u16::from_be_bytes([bytes[2], bytes[3]]);
let mut ip = [0u8; 16];
ip[0..4].copy_from_slice(&bytes[4..8]);
Some(Addr::Inet(InetAddr {
v6: false,
port,
ip,
}))
}
AF_INET6 if bytes.len() >= 24 => {
let port = u16::from_be_bytes([bytes[2], bytes[3]]);
let mut ip = [0u8; 16];
ip.copy_from_slice(&bytes[8..24]);
Some(Addr::Inet(InetAddr { v6: true, port, ip }))
}
_ => None,
}
}
fn encode_inet_sockaddr(a: InetAddr) -> Vec<u8> {
if a.v6 {
let mut b = vec![0u8; 28];
b[0..2].copy_from_slice(&AF_INET6.to_le_bytes());
b[2..4].copy_from_slice(&a.port.to_be_bytes());
b[8..24].copy_from_slice(&a.ip);
b
} else {
let mut b = vec![0u8; 16];
b[0..2].copy_from_slice(&AF_INET.to_le_bytes());
b[2..4].copy_from_slice(&a.port.to_be_bytes());
b[4..8].copy_from_slice(&a.ip[0..4]);
b
}
}
fn write_sockaddr(
mem: &mut GuestMemory,
addr: u64,
addrlen_ptr: u64,
domain: u16,
resolved: Option<&Addr>,
) -> i64 {
if addrlen_ptr == 0 {
return 0;
}
let buf = match resolved {
Some(Addr::Unix(name)) => {
let mut b = domain.to_le_bytes().to_vec();
b.extend_from_slice(name.as_bytes());
b.push(0);
b
}
Some(Addr::Inet(a)) => encode_inet_sockaddr(*a),
None => match domain {
AF_INET => encode_inet_sockaddr(InetAddr {
v6: false,
port: 0,
ip: [0; 16],
}),
AF_INET6 => encode_inet_sockaddr(InetAddr {
v6: true,
port: 0,
ip: [0; 16],
}),
_ => domain.to_le_bytes().to_vec(),
},
};
let cap = mem.read_u32(addrlen_ptr).unwrap_or(0) as usize;
if addr != 0 {
let n = buf.len().min(cap);
let _ = mem.write(addr, &buf[..n]);
}
let _ = mem.write(addrlen_ptr, &(buf.len() as u32).to_le_bytes());
0
}
fn write_optval(mem: &mut GuestMemory, optval: u64, optlen_ptr: u64, value: &[u8]) -> i64 {
if optval != 0 {
let _ = mem.write(optval, value);
}
if optlen_ptr != 0 {
let _ = mem.write(optlen_ptr, &(value.len() as u32).to_le_bytes());
}
0
}
fn nlmsg_align(len: usize) -> usize {
(len + 3) & !3
}
fn encode_nlmsg(nlmsg_type: u16, flags: u16, seq: u32, pid: u32, payload: &[u8]) -> Vec<u8> {
let total = 16 + payload.len();
let mut b = vec![0u8; nlmsg_align(total)];
b[0..4].copy_from_slice(&(total as u32).to_le_bytes());
b[4..6].copy_from_slice(&nlmsg_type.to_le_bytes());
b[6..8].copy_from_slice(&flags.to_le_bytes());
b[8..12].copy_from_slice(&seq.to_le_bytes());
b[12..16].copy_from_slice(&pid.to_le_bytes());
b[16..total].copy_from_slice(payload);
b
}
fn encode_rtattr(rta_type: u16, data: &[u8]) -> Vec<u8> {
let len = 4 + data.len();
let mut b = vec![0u8; nlmsg_align(len)];
b[0..2].copy_from_slice(&(len as u16).to_le_bytes());
b[2..4].copy_from_slice(&rta_type.to_le_bytes());
b[4..4 + data.len()].copy_from_slice(data);
b
}
fn encode_nlmsgerr(error: i32, orig_hdr: &[u8; 16], seq: u32, pid: u32) -> Vec<u8> {
let mut payload = vec![0u8; 4 + 16];
payload[0..4].copy_from_slice(&error.to_le_bytes());
payload[4..20].copy_from_slice(orig_hdr);
encode_nlmsg(NLMSG_ERROR, 0, seq, pid, &payload)
}
fn build_rtm_newlink(seq: u32, pid: u32) -> Vec<u8> {
let flags = IFF_UP | IFF_LOOPBACK | IFF_RUNNING;
let mut payload = vec![0u8; 16]; payload[2..4].copy_from_slice(&ARPHRD_LOOPBACK.to_le_bytes());
payload[4..8].copy_from_slice(&LOOPBACK_IFINDEX.to_le_bytes());
payload[8..12].copy_from_slice(&flags.to_le_bytes());
payload.extend(encode_rtattr(IFLA_IFNAME, b"lo\0"));
payload.extend(encode_rtattr(IFLA_MTU, &65_536u32.to_le_bytes()));
payload.extend(encode_rtattr(IFLA_ADDRESS, &[0u8; 6]));
encode_nlmsg(RTM_NEWLINK, 0, seq, pid, &payload)
}
fn build_rtm_newaddr_v4(seq: u32, pid: u32) -> Vec<u8> {
let mut payload = vec![0u8; 8]; payload[0] = AF_INET as u8;
payload[1] = 8; payload[3] = RT_SCOPE_HOST;
payload[4..8].copy_from_slice(&(LOOPBACK_IFINDEX as u32).to_le_bytes());
let ip = [127u8, 0, 0, 1];
payload.extend(encode_rtattr(IFA_ADDRESS, &ip));
payload.extend(encode_rtattr(IFA_LOCAL, &ip));
payload.extend(encode_rtattr(IFA_LABEL, b"lo\0"));
encode_nlmsg(RTM_NEWADDR, 0, seq, pid, &payload)
}
fn build_rtm_newaddr_v6(seq: u32, pid: u32) -> Vec<u8> {
let mut payload = vec![0u8; 8]; payload[0] = AF_INET6 as u8;
payload[1] = 128; payload[4..8].copy_from_slice(&(LOOPBACK_IFINDEX as u32).to_le_bytes());
let ip = loopback_ip(true);
payload.extend(encode_rtattr(IFA_ADDRESS, &ip));
payload.extend(encode_rtattr(IFA_LOCAL, &ip));
payload.extend(encode_rtattr(IFA_LABEL, b"lo\0"));
encode_nlmsg(RTM_NEWADDR, 0, seq, pid, &payload)
}
fn write_netlink_sockaddr(mem: &mut GuestMemory, addr: u64, addrlen_ptr: u64, pid: u32) -> i64 {
if addrlen_ptr == 0 {
return 0;
}
let mut b = [0u8; 12];
b[0..2].copy_from_slice(&AF_NETLINK.to_le_bytes());
b[4..8].copy_from_slice(&pid.to_le_bytes());
let cap = mem.read_u32(addrlen_ptr).unwrap_or(0) as usize;
if addr != 0 {
let n = b.len().min(cap);
let _ = mem.write(addr, &b[..n]);
}
let _ = mem.write(addrlen_ptr, &(b.len() as u32).to_le_bytes());
0
}
#[cfg(test)]
mod tests {
use super::*;
use crate::abi::Arch;
use crate::abi::arch::Sysno;
use crate::fs::{MountTable, TmpFs};
use crate::vcpu::mem::Prot;
use crate::vcpu::{Exit, Vcpu, VcpuError};
#[derive(Clone)]
struct DummyVcpu;
impl Vcpu for DummyVcpu {
fn run(&mut self, _m: &mut GuestMemory) -> Result<Exit, VcpuError> {
Ok(Exit::Halt)
}
fn syscall_nr(&self) -> u64 {
0
}
fn syscall_args(&self) -> [u64; 6] {
[0; 6]
}
fn set_syscall_ret(&mut self, _v: u64) {}
fn reg(&self, _i: usize) -> u64 {
0
}
fn set_reg(&mut self, _i: usize, _v: u64) {}
fn pc(&self) -> u64 {
0
}
fn set_pc(&mut self, _v: u64) {}
fn sp(&self) -> u64 {
0
}
fn set_sp(&mut self, _v: u64) {}
fn set_tls(&mut self, _v: u64) {}
fn fork(&self) -> Box<dyn Vcpu> {
Box::new(self.clone())
}
fn reset(&mut self, _e: u64, _s: u64) {}
}
const PAGE: u64 = 4096;
fn setup() -> (Kernel, GuestMemory, DummyVcpu) {
let mut mounts = MountTable::new();
mounts.mount("/", Box::new(TmpFs::new()));
let mut kernel = Kernel::new(Arch::Aarch64, mounts);
kernel.cur.pid = 1;
let mut mem = GuestMemory::new(0x1_0000, 16 * PAGE);
mem.map(0x1_0000, 4 * PAGE, Prot::rw()).unwrap();
(kernel, mem, DummyVcpu)
}
fn call(
k: &mut Kernel,
mem: &mut GuestMemory,
v: &mut DummyVcpu,
s: Sysno,
a: [u64; 6],
) -> i64 {
k.dispatch(s, 0, &a, v, mem)
}
fn write_sockaddr_in(mem: &mut GuestMemory, ptr: u64, ip: [u8; 4], port: u16) {
let mut b = [0u8; 16];
b[0..2].copy_from_slice(&2u16.to_le_bytes());
b[2..4].copy_from_slice(&port.to_be_bytes());
b[4..8].copy_from_slice(&ip);
mem.write_init(ptr, &b).unwrap();
}
fn write_sockaddr_in6(mem: &mut GuestMemory, ptr: u64, ip: [u8; 16], port: u16) {
let mut b = [0u8; 28];
b[0..2].copy_from_slice(&10u16.to_le_bytes());
b[2..4].copy_from_slice(&port.to_be_bytes());
b[8..24].copy_from_slice(&ip);
mem.write_init(ptr, &b).unwrap();
}
fn read_port(mem: &GuestMemory, ptr: u64) -> u16 {
let b = mem.read_vec(ptr, 4).unwrap();
u16::from_be_bytes([b[2], b[3]])
}
#[test]
fn socketpair_roundtrip() {
let (mut k, mut mem, mut v) = setup();
let sv = 0x1_0000;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Socketpair,
[1, 1, 0, sv, 0, 0]
),
0
);
let a = u64::from(mem.read_u32(sv).unwrap());
let b = u64::from(mem.read_u32(sv + 4).unwrap());
assert!(a >= 3 && b >= 3 && a != b);
let msg = 0x1_1000;
let out = 0x1_2000;
mem.write_init(msg, b"hi").unwrap();
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Write, [a, msg, 2, 0, 0, 0]),
2
);
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [b, out, 2, 0, 0, 0]),
2
);
assert_eq!(mem.read_vec(out, 2).unwrap(), b"hi");
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [a, out, 2, 0, 0, 0]),
0
);
assert!(k.block);
}
#[test]
fn bind_listen_connect_accept_bidirectional() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
mem.write_init(addr, &1u16.to_le_bytes()).unwrap(); mem.write_init(addr + 2, b"/s\0").unwrap();
let alen = 5u64;
let srv = call(&mut k, &mut mem, &mut v, Sysno::Socket, [1, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, alen, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
let cli = call(&mut k, &mut mem, &mut v, Sysno::Socket, [1, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[cli, addr, alen, 0, 0, 0]
),
0
);
let acc = call(
&mut k,
&mut mem,
&mut v,
Sysno::Accept4,
[srv, 0, 0, 0, 0, 0],
);
assert!(acc >= 3, "accept returned a fd");
let acc = acc as u64;
let msg = 0x1_2000;
let out = 0x1_3000;
mem.write_init(msg, b"ping").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[cli, msg, 4, 0, 0, 0]
),
4
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[acc, out, 4, 0, 0, 0]
),
4
);
assert_eq!(mem.read_vec(out, 4).unwrap(), b"ping");
mem.write_init(msg, b"pong").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[acc, msg, 4, 0, 0, 0]
),
4
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[cli, out, 4, 0, 0, 0]
),
4
);
assert_eq!(mem.read_vec(out, 4).unwrap(), b"pong");
}
#[test]
fn connect_without_listener_is_refused() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
mem.write_init(addr, &1u16.to_le_bytes()).unwrap();
mem.write_init(addr + 2, b"/nope\0").unwrap();
let cli = call(&mut k, &mut mem, &mut v, Sysno::Socket, [1, 1, 0, 0, 0, 0]) as u64;
let ret = call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[cli, addr, 8, 0, 0, 0],
);
assert_eq!(ret, -i64::from(Errno::ECONNREFUSED.0));
}
#[test]
fn write_to_socket_with_closed_peer_is_epipe() {
let (mut k, mut mem, mut v) = setup();
let sv = 0x1_0000;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Socketpair,
[1, 1, 0, sv, 0, 0]
),
0
);
let end0 = u64::from(mem.read_u32(sv).unwrap());
let end1 = u64::from(mem.read_u32(sv + 4).unwrap());
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Close,
[end1, 0, 0, 0, 0, 0]
),
0
);
let msg = 0x1_1000;
mem.write_init(msg, b"x").unwrap();
let ret = call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[end0, msg, 1, 0, 0, 0],
);
assert_eq!(ret, -i64::from(Errno::EPIPE.0));
}
#[test]
fn fstat_reports_socket_type() {
let (mut k, mut mem, mut v) = setup();
let sv = 0x1_0000;
call(
&mut k,
&mut mem,
&mut v,
Sysno::Socketpair,
[1, 1, 0, sv, 0, 0],
);
let a = u64::from(mem.read_u32(sv).unwrap());
let st = 0x1_2000;
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Fstat, [a, st, 0, 0, 0, 0]),
0
);
let mode = mem.read_u32(st + 16).unwrap();
assert_eq!(mode & 0o170_000, 0o140_000, "S_IFSOCK");
}
#[test]
#[allow(clippy::too_many_lines)] fn tcp_inet4_loopback_roundtrip() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
write_sockaddr_in(&mut mem, addr, [127, 0, 0, 1], 9000);
let alen = 16u64;
let srv = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, alen, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
let cli = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[cli, addr, alen, 0, 0, 0]
),
0
);
let acc = call(
&mut k,
&mut mem,
&mut v,
Sysno::Accept4,
[srv, 0, 0, 0, 0, 0],
);
assert!(acc >= 3, "accept returned a fd");
let acc = acc as u64;
let msg = 0x1_1200;
let out = 0x1_1300;
mem.write_init(msg, b"ping").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[cli, msg, 4, 0, 0, 0]
),
4
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[acc, out, 4, 0, 0, 0]
),
4
);
assert_eq!(mem.read_vec(out, 4).unwrap(), b"ping");
mem.write_init(msg, b"pong").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[acc, msg, 4, 0, 0, 0]
),
4
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[cli, out, 4, 0, 0, 0]
),
4
);
assert_eq!(mem.read_vec(out, 4).unwrap(), b"pong");
let peer = 0x1_1400;
let peerlen = 0x1_1500;
mem.write_init(peerlen, &16u32.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Getpeername,
[cli, peer, peerlen, 0, 0, 0]
),
0
);
assert_eq!(read_port(&mem, peer), 9000);
assert_eq!(mem.read_vec(peer, 8).unwrap()[4..8], [127, 0, 0, 1]);
mem.write_init(peerlen, &16u32.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Getsockname,
[acc, peer, peerlen, 0, 0, 0]
),
0
);
assert_eq!(read_port(&mem, peer), 9000);
}
#[test]
fn tcp_inet6_loopback_roundtrip() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
let mut ip = [0u8; 16];
ip[15] = 1; write_sockaddr_in6(&mut mem, addr, ip, 9700);
let alen = 28u64;
let srv = call(&mut k, &mut mem, &mut v, Sysno::Socket, [10, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, alen, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
let cli = call(&mut k, &mut mem, &mut v, Sysno::Socket, [10, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[cli, addr, alen, 0, 0, 0]
),
0
);
let acc = call(
&mut k,
&mut mem,
&mut v,
Sysno::Accept4,
[srv, 0, 0, 0, 0, 0],
);
assert!(acc >= 3, "accept returned a fd");
let acc = acc as u64;
let msg = 0x1_1200;
let out = 0x1_1300;
mem.write_init(msg, b"v6ok").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[cli, msg, 4, 0, 0, 0]
),
4
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[acc, out, 4, 0, 0, 0]
),
4
);
assert_eq!(mem.read_vec(out, 4).unwrap(), b"v6ok");
}
#[test]
fn ephemeral_port_via_getsockname() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
write_sockaddr_in(&mut mem, addr, [127, 0, 0, 1], 0); let s = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[s, addr, 16, 0, 0, 0]
),
0
);
let name = 0x1_1200;
let namelen = 0x1_1300;
mem.write_init(namelen, &16u32.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Getsockname,
[s, name, namelen, 0, 0, 0]
),
0
);
assert!(read_port(&mem, name) >= 32_768);
}
#[test]
fn udp_connected_roundtrip_via_dispatch() {
let (mut k, mut mem, mut v) = setup();
let a_addr = 0x1_1000;
write_sockaddr_in(&mut mem, a_addr, [127, 0, 0, 1], 9300);
let b_addr = 0x1_1100;
write_sockaddr_in(&mut mem, b_addr, [127, 0, 0, 1], 9400);
let a = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 2, 0, 0, 0, 0]) as u64;
let b = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 2, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[a, a_addr, 16, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[b, b_addr, 16, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[a, b_addr, 16, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[b, a_addr, 16, 0, 0, 0]
),
0
);
let msg = 0x1_1200;
let out = 0x1_1300;
mem.write_init(msg, b"hi").unwrap();
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Write, [a, msg, 2, 0, 0, 0]),
2
);
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [b, out, 2, 0, 0, 0]),
2
);
assert_eq!(mem.read_vec(out, 2).unwrap(), b"hi");
}
#[test]
fn udp_sendto_recvfrom_with_source_addr() {
let (mut k, mut mem, _v) = setup();
let a_addr = 0x1_1000;
write_sockaddr_in(&mut mem, a_addr, [127, 0, 0, 1], 9100);
let b_addr = 0x1_1100;
write_sockaddr_in(&mut mem, b_addr, [127, 0, 0, 1], 9200);
let a = k.sys_socket(2, 2, 0) as u64; let b = k.sys_socket(2, 2, 0) as u64;
assert_eq!(k.sys_bind(a, a_addr, 16, &mem), 0);
assert_eq!(k.sys_bind(b, b_addr, 16, &mem), 0);
let msg = 0x1_1200;
mem.write_init(msg, b"hello").unwrap();
assert_eq!(k.sys_sendto(a, msg, 5, 0, b_addr, 16, &mem), 5);
let out = 0x1_1300;
let src = 0x1_1400;
let srclen = 0x1_1500;
mem.write_init(srclen, &16u32.to_le_bytes()).unwrap();
assert_eq!(k.sys_recvfrom(b, out, 5, 0, src, srclen, &mut mem), 5);
assert_eq!(mem.read_vec(out, 5).unwrap(), b"hello");
assert_eq!(read_port(&mem, src), 9100); assert_eq!(mem.read_vec(src, 8).unwrap()[4..8], [127, 0, 0, 1]);
}
#[test]
fn setsockopt_reuseaddr_allows_rebind() {
let (mut k, mut mem, _v) = setup();
let addr = 0x1_1000;
write_sockaddr_in(&mut mem, addr, [127, 0, 0, 1], 9500);
let a = k.sys_socket(2, 1, 0) as u64;
assert_eq!(k.sys_bind(a, addr, 16, &mem), 0);
let b = k.sys_socket(2, 1, 0) as u64;
assert_eq!(k.sys_bind(b, addr, 16, &mem), -i64::from(Errno::EINVAL.0));
let optval = 0x1_1600;
mem.write_init(optval, &1u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_setsockopt(b, SOL_SOCKET, SO_REUSEADDR, optval, 4, &mem),
0
);
assert_eq!(k.sys_bind(b, addr, 16, &mem), 0);
}
#[test]
fn accept4_nonblocking_returns_eagain() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
write_sockaddr_in(&mut mem, addr, [127, 0, 0, 1], 9600);
let srv = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[2, 1 | SOCK_NONBLOCK, 0, 0, 0, 0],
) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, 16, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
let ret = call(
&mut k,
&mut mem,
&mut v,
Sysno::Accept4,
[srv, 0, 0, 0, 0, 0],
);
assert_eq!(ret, -i64::from(Errno::EAGAIN.0));
assert!(!k.block);
}
#[test]
fn setsockopt_getsockopt_rcvbuf_and_reuseaddr_roundtrip() {
let (mut k, mut mem, _v) = setup();
let s = k.sys_socket(2, 1, 0) as u64;
let optval = 0x1_1000;
mem.write_init(optval, &65_536u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_setsockopt(s, SOL_SOCKET, SO_RCVBUF, optval, 4, &mem),
0
);
mem.write_init(optval, &1u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, optval, 4, &mem),
0
);
let out = 0x1_1100;
let outlen = 0x1_1200;
mem.write_init(outlen, &4u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_getsockopt(s, SOL_SOCKET, SO_RCVBUF, out, outlen, &mut mem),
0
);
assert_eq!(mem.read_u32(out).unwrap(), 65_536);
mem.write_init(outlen, &4u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_getsockopt(s, SOL_SOCKET, SO_REUSEADDR, out, outlen, &mut mem),
0
);
assert_eq!(mem.read_u32(out).unwrap(), 1);
}
#[test]
fn so_acceptconn_is_one_after_listen() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
write_sockaddr_in(&mut mem, addr, [127, 0, 0, 1], 9800);
let srv = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, 16, 0, 0, 0]
),
0
);
let out = 0x1_1100;
let outlen = 0x1_1200;
mem.write_init(outlen, &4u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_getsockopt(srv, SOL_SOCKET, SO_ACCEPTCONN, out, outlen, &mut mem),
0
);
assert_eq!(mem.read_u32(out).unwrap(), 0, "not listening yet");
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
mem.write_init(outlen, &4u32.to_le_bytes()).unwrap();
assert_eq!(
k.sys_getsockopt(srv, SOL_SOCKET, SO_ACCEPTCONN, out, outlen, &mut mem),
0
);
assert_eq!(mem.read_u32(out).unwrap(), 1, "listening");
}
#[test]
fn msg_peek_returns_same_bytes_twice() {
let (mut k, mut mem, mut v) = setup();
let sv = 0x1_0000;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Socketpair,
[1, 1, 0, sv, 0, 0]
),
0
);
let a = u64::from(mem.read_u32(sv).unwrap());
let b = u64::from(mem.read_u32(sv + 4).unwrap());
let msg = 0x1_1000;
mem.write_init(msg, b"peekme").unwrap();
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Write, [a, msg, 6, 0, 0, 0]),
6
);
let out = 0x1_2000;
assert_eq!(k.sys_recvfrom(b, out, 6, MSG_PEEK, 0, 0, &mut mem), 6);
assert_eq!(mem.read_vec(out, 6).unwrap(), b"peekme");
assert_eq!(k.sys_recvfrom(b, out, 6, MSG_PEEK, 0, 0, &mut mem), 6);
assert_eq!(mem.read_vec(out, 6).unwrap(), b"peekme");
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [b, out, 6, 0, 0, 0]),
6
);
assert_eq!(mem.read_vec(out, 6).unwrap(), b"peekme");
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [b, out, 6, 0, 0, 0]),
0
);
assert!(k.block);
}
#[test]
fn af_unix_abstract_namespace_bind_connect_exchange() {
let (mut k, mut mem, mut v) = setup();
let addr = 0x1_1000;
mem.write_init(addr, &1u16.to_le_bytes()).unwrap(); mem.write_init(addr + 2, b"\0nixvm").unwrap();
let alen = 2 + 6;
let srv = call(&mut k, &mut mem, &mut v, Sysno::Socket, [1, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[srv, addr, alen, 0, 0, 0]
),
0
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Listen,
[srv, 8, 0, 0, 0, 0]
),
0
);
let cli = call(&mut k, &mut mem, &mut v, Sysno::Socket, [1, 1, 0, 0, 0, 0]) as u64;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Connect,
[cli, addr, alen, 0, 0, 0]
),
0
);
let acc = call(
&mut k,
&mut mem,
&mut v,
Sysno::Accept4,
[srv, 0, 0, 0, 0, 0],
);
assert!(acc >= 3, "accept returned a fd");
let acc = acc as u64;
let msg = 0x1_2000;
let out = 0x1_3000;
mem.write_init(msg, b"hi").unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[cli, msg, 2, 0, 0, 0]
),
2
);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[acc, out, 2, 0, 0, 0]
),
2
);
assert_eq!(mem.read_vec(out, 2).unwrap(), b"hi");
}
#[test]
fn shutdown_wr_then_peer_read_sees_eof() {
const SHUT_WR: u64 = 1;
let (mut k, mut mem, mut v) = setup();
let sv = 0x1_0000;
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Socketpair,
[1, 1, 0, sv, 0, 0]
),
0
);
let a = u64::from(mem.read_u32(sv).unwrap());
let b = u64::from(mem.read_u32(sv + 4).unwrap());
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Shutdown,
[a, SHUT_WR, 0, 0, 0, 0]
),
0
);
let out = 0x1_1000;
assert_eq!(
call(&mut k, &mut mem, &mut v, Sysno::Read, [b, out, 4, 0, 0, 0]),
0
);
assert!(!k.block);
let msg = 0x1_2000;
mem.write_init(msg, b"x").unwrap();
let ret = call(&mut k, &mut mem, &mut v, Sysno::Write, [a, msg, 1, 0, 0, 0]);
assert_eq!(ret, -i64::from(Errno::EPIPE.0));
}
#[test]
fn getpeername_on_unconnected_returns_enotconn() {
let (mut k, mut mem, mut v) = setup();
let s = call(&mut k, &mut mem, &mut v, Sysno::Socket, [2, 1, 0, 0, 0, 0]) as u64;
let peer = 0x1_1000;
let peerlen = 0x1_1100;
mem.write_init(peerlen, &16u32.to_le_bytes()).unwrap();
let ret = call(
&mut k,
&mut mem,
&mut v,
Sysno::Getpeername,
[s, peer, peerlen, 0, 0, 0],
);
assert_eq!(ret, -i64::from(Errno::ENOTCONN.0));
}
const NLM_F_REQUEST: u16 = 0x01;
fn write_nlmsghdr(mem: &mut GuestMemory, ptr: u64, nlmsg_type: u16, flags: u16, seq: u32) {
let mut b = [0u8; 16];
b[0..4].copy_from_slice(&16u32.to_le_bytes());
b[4..6].copy_from_slice(&nlmsg_type.to_le_bytes());
b[6..8].copy_from_slice(&flags.to_le_bytes());
b[8..12].copy_from_slice(&seq.to_le_bytes());
mem.write_init(ptr, &b).unwrap();
}
fn parse_nlmsgs(buf: &[u8]) -> Vec<(u16, u32, Vec<u8>)> {
let mut out = Vec::new();
let mut off = 0usize;
while off + 16 <= buf.len() {
let len = u32::from_le_bytes(buf[off..off + 4].try_into().unwrap()) as usize;
assert!(len >= 16, "nlmsg_len must cover at least the header");
let ty = u16::from_le_bytes(buf[off + 4..off + 6].try_into().unwrap());
let seq = u32::from_le_bytes(buf[off + 8..off + 12].try_into().unwrap());
let payload = buf[off + 16..off + len].to_vec();
out.push((ty, seq, payload));
let aligned = (len + 3) & !3;
assert_eq!(aligned % 4, 0, "NLMSG_ALIGN must land on a 4-byte boundary");
off += aligned;
}
assert_eq!(off, buf.len(), "messages must exactly tile the buffer");
out
}
fn find_rtattr(payload: &[u8], hdr_len: usize, rta_type: u16) -> Option<Vec<u8>> {
let mut off = hdr_len;
while off + 4 <= payload.len() {
let len = u16::from_le_bytes(payload[off..off + 2].try_into().unwrap()) as usize;
assert!(len >= 4, "rtattr len must cover at least its own header");
let ty = u16::from_le_bytes(payload[off + 2..off + 4].try_into().unwrap());
let data = payload[off + 4..off + len].to_vec();
let aligned = (len + 3) & !3;
assert_eq!(aligned % 4, 0, "RTA_ALIGN must land on a 4-byte boundary");
if ty == rta_type {
return Some(data);
}
off += aligned;
}
None
}
#[test]
fn netlink_getlink_dump_reports_lo() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[
u64::from(AF_NETLINK),
SOCK_RAW | SOCK_NONBLOCK,
NETLINK_ROUTE,
0,
0,
0,
],
) as u64;
assert!(fd >= 3);
let req = 0x1_1000;
write_nlmsghdr(&mut mem, req, RTM_GETLINK, NLM_F_REQUEST | NLM_F_DUMP, 42);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[fd, req, 16, 0, 0, 0]
),
16
);
let out = 0x1_2000;
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[fd, out, 2048, 0, 0, 0],
);
assert!(n > 0, "expected a reply");
let buf = mem.read_vec(out, n as usize).unwrap();
let msgs = parse_nlmsgs(&buf);
assert_eq!(msgs.len(), 2, "one RTM_NEWLINK, then NLMSG_DONE");
let (ty, seq, payload) = &msgs[0];
assert_eq!(*ty, RTM_NEWLINK);
assert_eq!(*seq, 42);
let ifname = find_rtattr(payload, 16, IFLA_IFNAME).expect("IFLA_IFNAME present");
assert_eq!(ifname, b"lo\0");
let (ty, seq, _) = &msgs[1];
assert_eq!(*ty, NLMSG_DONE);
assert_eq!(*seq, 42);
}
#[test]
fn netlink_getaddr_dump_reports_127_0_0_1() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[
u64::from(AF_NETLINK),
SOCK_RAW | SOCK_NONBLOCK,
NETLINK_ROUTE,
0,
0,
0,
],
) as u64;
let req = 0x1_1000;
write_nlmsghdr(&mut mem, req, RTM_GETADDR, NLM_F_REQUEST | NLM_F_DUMP, 7);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Sendto,
[fd, req, 16, 0, 0, 0]
),
16
);
let out = 0x1_2000;
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::Recvfrom,
[fd, out, 2048, 0, 0, 0],
);
assert!(n > 0, "expected a reply");
let buf = mem.read_vec(out, n as usize).unwrap();
let msgs = parse_nlmsgs(&buf);
assert_eq!(*msgs.last().map(|(ty, ..)| ty).unwrap(), NLMSG_DONE);
let v4 = msgs
.iter()
.find(|(ty, ..)| *ty == RTM_NEWADDR)
.and_then(|(_, _, payload)| find_rtattr(payload, 8, IFA_LOCAL))
.expect("an RTM_NEWADDR with IFA_LOCAL");
assert_eq!(v4, [127, 0, 0, 1]);
}
#[test]
fn netlink_unknown_type_yields_nlmsg_error() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[
u64::from(AF_NETLINK),
SOCK_RAW | SOCK_NONBLOCK,
NETLINK_ROUTE,
0,
0,
0,
],
) as u64;
let req = 0x1_1000;
write_nlmsghdr(&mut mem, req, 0xffff, NLM_F_REQUEST, 99);
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Write,
[fd, req, 16, 0, 0, 0]
),
16
);
let out = 0x1_2000;
let n = call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[fd, out, 2048, 0, 0, 0],
);
assert!(n > 0);
let buf = mem.read_vec(out, n as usize).unwrap();
let msgs = parse_nlmsgs(&buf);
assert_eq!(msgs.len(), 1);
let (ty, seq, payload) = &msgs[0];
assert_eq!(*ty, NLMSG_ERROR);
assert_eq!(*seq, 99);
let error = i32::from_le_bytes(payload[0..4].try_into().unwrap());
assert_eq!(error, -Errno::EOPNOTSUPP.0);
}
#[test]
fn netlink_bind_and_getsockname_roundtrip_pid() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[
u64::from(AF_NETLINK),
SOCK_RAW | SOCK_NONBLOCK,
NETLINK_ROUTE,
0,
0,
0,
],
) as u64;
let addr = 0x1_1000;
let mut b = [0u8; 12];
b[0..2].copy_from_slice(&AF_NETLINK.to_le_bytes());
b[4..8].copy_from_slice(&4242u32.to_le_bytes());
mem.write_init(addr, &b).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Bind,
[fd, addr, 12, 0, 0, 0]
),
0
);
let name = 0x1_2000;
let namelen = 0x1_2100;
mem.write_init(namelen, &12u32.to_le_bytes()).unwrap();
assert_eq!(
call(
&mut k,
&mut mem,
&mut v,
Sysno::Getsockname,
[fd, name, namelen, 0, 0, 0]
),
0
);
let b = mem.read_vec(name, 8).unwrap();
assert_eq!(u16::from_le_bytes([b[0], b[1]]), AF_NETLINK);
assert_eq!(u32::from_le_bytes([b[4], b[5], b[6], b[7]]), 4242);
}
#[test]
fn netlink_nonblocking_recv_with_empty_queue_is_eagain() {
let (mut k, mut mem, mut v) = setup();
let fd = call(
&mut k,
&mut mem,
&mut v,
Sysno::Socket,
[
u64::from(AF_NETLINK),
SOCK_RAW | SOCK_NONBLOCK,
NETLINK_ROUTE,
0,
0,
0,
],
) as u64;
let out = 0x1_1000;
let ret = call(
&mut k,
&mut mem,
&mut v,
Sysno::Read,
[fd, out, 64, 0, 0, 0],
);
assert_eq!(ret, -i64::from(Errno::EAGAIN.0));
assert!(!k.block);
}
}