Skip to content

Commit 7464ab5

Browse files
committed
Implement Netlink bind syscall
According to netlink(7), there are two issues worth mentioning. If the pid of the address is zero, the kernel takes care of assigning it, but we will leave it untouched at the moment. We can implement the assignment later when we want to support it. If the groups of the address is non-zero, it means that the socket wants to listen to some groups. Since we don't support broadcasting to groups yet, we will emit the error intead.
1 parent 7de08d3 commit 7464ab5

3 files changed

Lines changed: 189 additions & 5 deletions

File tree

src/main/host/descriptor/socket/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ impl Socket {
9292
match self {
9393
Self::Unix(socket) => UnixSocket::bind(socket, addr, net_ns, rng),
9494
Self::Inet(socket) => InetSocket::bind(socket, addr, net_ns, rng),
95-
Self::Netlink(_) => todo!(),
95+
Self::Netlink(socket) => NetlinkSocket::bind(socket, addr, net_ns, rng),
9696
}
9797
}
9898

src/main/host/descriptor/socket/netlink.rs

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ use crate::host::descriptor::{
1111
FileMode, FileState, FileStatus, StateListenerFilter, SyscallResult,
1212
};
1313
use crate::host::memory_manager::MemoryManager;
14+
use crate::host::network::namespace::NetworkNamespace;
1415
use crate::host::syscall::io::IoVec;
1516
use crate::host::syscall_types::SyscallError;
1617
use crate::utility::callback_queue::{CallbackQueue, Handle};
18+
use crate::utility::sockaddr::SockaddrStorage;
1719
use crate::utility::HostTreePointer;
1820

1921
pub struct NetlinkSocket {
@@ -112,6 +114,18 @@ impl NetlinkSocket {
112114
Err(Errno::ENOSYS.into())
113115
}
114116

117+
pub fn bind(
118+
socket: &Arc<AtomicRefCell<Self>>,
119+
addr: Option<&SockaddrStorage>,
120+
_net_ns: &NetworkNamespace,
121+
rng: impl rand::Rng,
122+
) -> SyscallResult {
123+
let socket_ref = &mut *socket.borrow_mut();
124+
socket_ref
125+
.protocol_state
126+
.bind(&mut socket_ref.common, socket, addr, rng)
127+
}
128+
115129
pub fn readv(
116130
&mut self,
117131
_iovs: &[IoVec],
@@ -173,7 +187,11 @@ impl NetlinkSocket {
173187
}
174188
}
175189

176-
struct InitialState {}
190+
struct InitialState {
191+
// Indicate that if the socket is already bound or not. We don't keep the bound address so that
192+
// we won't need to fill it.
193+
is_bound: bool,
194+
}
177195
struct ClosedState {}
178196
/// The current protocol state of the netlink socket. An `Option` is required for each variant so that
179197
/// the inner state object can be removed, transformed into a new state, and then re-added as a
@@ -200,7 +218,77 @@ state_upcast!(ClosedState, ProtocolState::Closed);
200218

201219
impl ProtocolState {
202220
fn new(common: &mut NetlinkSocketCommon, socket: &Weak<AtomicRefCell<NetlinkSocket>>) -> Self {
203-
ProtocolState::Initial(Some(InitialState {}))
221+
ProtocolState::Initial(Some(InitialState { is_bound: false }))
222+
}
223+
224+
fn bind(
225+
&mut self,
226+
common: &mut NetlinkSocketCommon,
227+
socket: &Arc<AtomicRefCell<NetlinkSocket>>,
228+
addr: Option<&SockaddrStorage>,
229+
rng: impl rand::Rng,
230+
) -> SyscallResult {
231+
match self {
232+
Self::Initial(x) => x.as_mut().unwrap().bind(common, socket, addr, rng),
233+
Self::Closed(x) => x.as_mut().unwrap().bind(common, socket, addr, rng),
234+
}
235+
}
236+
}
237+
238+
impl InitialState {
239+
fn bind(
240+
&mut self,
241+
_common: &mut NetlinkSocketCommon,
242+
_socket: &Arc<AtomicRefCell<NetlinkSocket>>,
243+
addr: Option<&SockaddrStorage>,
244+
_rng: impl rand::Rng,
245+
) -> SyscallResult {
246+
// if already bound
247+
if self.is_bound {
248+
return Err(Errno::EINVAL.into());
249+
}
250+
251+
// get the netlink address
252+
let Some(addr) = addr.and_then(|x| x.as_netlink()) else {
253+
log::warn!(
254+
"Attempted to bind netlink socket to non-netlink address {:?}",
255+
addr
256+
);
257+
return Err(Errno::EINVAL.into());
258+
};
259+
// remember that the socket is bound
260+
self.is_bound = true;
261+
262+
// According to netlink(7), if the pid is zero, the kernel takes care of assigning it, but
263+
// we will leave it untouched at the moment. We can implement the assignment later when we
264+
// want to support it.
265+
266+
// According to netlink(7), if the groups is non-zero, it means that the socket wants to
267+
// listen to some groups. Since we don't support broadcasting to groups yet, we will emit
268+
// the error here.
269+
if addr.groups() != 0 {
270+
log::warn!(
271+
"Attempted to bind netlink socket to an address with non-zero groups {}",
272+
addr.groups()
273+
);
274+
return Err(Errno::EINVAL.into());
275+
}
276+
277+
Ok(0.into())
278+
}
279+
}
280+
281+
impl ClosedState {
282+
fn bind(
283+
&mut self,
284+
_common: &mut NetlinkSocketCommon,
285+
_socket: &Arc<AtomicRefCell<NetlinkSocket>>,
286+
_addr: Option<&SockaddrStorage>,
287+
_rng: impl rand::Rng,
288+
) -> SyscallResult {
289+
// We follow the same approach as UnixSocket
290+
log::warn!("bind() while in state {}", std::any::type_name::<Self>());
291+
Err(Errno::EOPNOTSUPP.into())
204292
}
205293
}
206294

src/main/utility/sockaddr.rs

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ union Addr {
2222
inet: libc::sockaddr_in,
2323
inet6: libc::sockaddr_in6,
2424
unix: libc::sockaddr_un,
25+
netlink: libc::sockaddr_nl,
2526
}
2627

2728
// verify there are no larger fields larger than `libc::sockaddr_storage`
@@ -162,6 +163,34 @@ impl SockaddrStorage {
162163
unsafe { Self::from_ptr(ptr as *const MaybeUninit<u8>, len) }.unwrap()
163164
}
164165

166+
/// If the socket address represents a valid netlink socket address (correct family and length),
167+
/// returns the netlink socket address.
168+
pub fn as_netlink(&self) -> Option<&nix::sys::socket::NetlinkAddr> {
169+
if (self.len as usize) < std::mem::size_of::<libc::sockaddr_nl>() {
170+
return None;
171+
}
172+
if self.family() != Some(AddressFamily::Netlink) {
173+
return None;
174+
}
175+
176+
// SAFETY: Assume that `nix::sys::socket::NetlinkAddr` is a transparent wrapper around a
177+
// `libc::sockaddr_nl`. Verify (as best we can) that this is true.
178+
assert_eq_size!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
179+
assert_eq_align!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
180+
181+
Some(unsafe { &*(&self.addr.netlink as *const _ as *const nix::sys::socket::NetlinkAddr) })
182+
}
183+
184+
/// Get a new `SockaddrStorage` with a copy of the netlink socket address.
185+
pub fn from_netlink(addr: &nix::sys::socket::NetlinkAddr) -> Self {
186+
// SAFETY: Assume that `nix::sys::socket::NetlinkAddr` is a transparent wrapper around a
187+
// `libc::sockaddr_nl`. Verify (as best we can) that this is true.
188+
assert_eq_size!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
189+
assert_eq_align!(libc::sockaddr_nl, nix::sys::socket::NetlinkAddr);
190+
191+
unsafe { Self::from_ptr(addr.as_ptr() as *const MaybeUninit<u8>, addr.len()) }.unwrap()
192+
}
193+
165194
/// A pointer to the socket address. Some bytes may be uninitialized.
166195
pub fn as_ptr(&self) -> (*const MaybeUninit<u8>, libc::socklen_t) {
167196
(unsafe { &self.addr.slice }.as_ptr(), self.len)
@@ -178,13 +207,15 @@ impl std::fmt::Debug for SockaddrStorage {
178207
let as_inet = self.as_inet();
179208
let as_inet6 = self.as_inet6();
180209
let as_unix = self.as_unix();
210+
let as_netlink = self.as_netlink();
181211

182212
let as_inet = as_inet.map(|x| x as &dyn std::fmt::Debug);
183213
let as_inet6 = as_inet6.map(|x| x as &dyn std::fmt::Debug);
184214
let as_unix = as_unix.as_ref().map(|x| x as &dyn std::fmt::Debug);
215+
let as_netlink = as_netlink.as_ref().map(|x| x as &dyn std::fmt::Debug);
185216

186217
// find a representation that is not None
187-
let options = [as_inet, as_inet6, as_unix];
218+
let options = [as_inet, as_inet6, as_unix, as_netlink];
188219
let addr = options.into_iter().find_map(std::convert::identity);
189220

190221
if let Some(ref addr) = addr {
@@ -206,13 +237,15 @@ impl std::fmt::Display for SockaddrStorage {
206237
let as_inet = self.as_inet();
207238
let as_inet6 = self.as_inet6();
208239
let as_unix = self.as_unix();
240+
let as_netlink = self.as_netlink();
209241

210242
let as_inet = as_inet.map(|x| x as &dyn std::fmt::Display);
211243
let as_inet6 = as_inet6.map(|x| x as &dyn std::fmt::Display);
212244
let as_unix = as_unix.as_ref().map(|x| x as &dyn std::fmt::Display);
245+
let as_netlink = as_netlink.as_ref().map(|x| x as &dyn std::fmt::Display);
213246

214247
// find a representation that is not None
215-
let options = [as_inet, as_inet6, as_unix];
248+
let options = [as_inet, as_inet6, as_unix, as_netlink];
216249
let addr = options.into_iter().find_map(std::convert::identity);
217250

218251
if let Some(ref addr) = addr {
@@ -259,6 +292,12 @@ impl From<std::net::SocketAddrV6> for SockaddrStorage {
259292
}
260293
}
261294

295+
impl From<nix::sys::socket::NetlinkAddr> for SockaddrStorage {
296+
fn from(addr: nix::sys::socket::NetlinkAddr) -> Self {
297+
SockaddrStorage::from_netlink(&addr)
298+
}
299+
}
300+
262301
/// A Unix socket address. Typically will be used as an owned address
263302
/// `SockaddrUnix<libc::sockaddr_un>` or a borrowed address `SockaddrUnix<&libc::sockaddr_un>`, and
264303
/// you can convert between them using methods such as [`as_ref`](Self::as_ref) or
@@ -529,6 +568,7 @@ mod tests {
529568
assert!(addr.as_inet().is_some());
530569
assert!(addr.as_inet6().is_none());
531570
assert!(addr.as_unix().is_none());
571+
assert!(addr.as_netlink().is_none());
532572
}
533573

534574
/// Convert from a `sockaddr_un` to a `SockaddrStorage`.
@@ -549,6 +589,27 @@ mod tests {
549589
assert!(addr.as_unix().is_some());
550590
assert!(addr.as_inet().is_none());
551591
assert!(addr.as_inet6().is_none());
592+
assert!(addr.as_netlink().is_none());
593+
}
594+
595+
/// Convert from a `sockaddr_nl` to a `SockaddrStorage`.
596+
#[test]
597+
fn storage_from_netlink_ptr() {
598+
let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
599+
addr.nl_family = libc::AF_NETLINK as u16;
600+
addr.nl_pid = 0x38deb915;
601+
addr.nl_groups = 0xf229a8ea;
602+
603+
let ptr = &addr as *const _ as *const MaybeUninit<u8>;
604+
let len = std::mem::size_of_val(&addr).try_into().unwrap();
605+
606+
let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
607+
608+
assert_eq!(addr.family(), Some(AddressFamily::Netlink));
609+
assert!(addr.as_netlink().is_some());
610+
assert!(addr.as_inet().is_none());
611+
assert!(addr.as_inet6().is_none());
612+
assert!(addr.as_unix().is_none());
552613
}
553614

554615
/// Convert from a `sockaddr_in` to a `SockaddrStorage` to a `SockaddrIn`.
@@ -588,6 +649,41 @@ mod tests {
588649
assert_eq!(u32::from_be(addr.sin_addr.s_addr), addr_original.ip());
589650
}
590651

652+
/// Convert from a `sockaddr_nl` to a `SockaddrStorage` to a `NetlinkAddr`.
653+
#[test]
654+
fn netlink_addr_from_libc() {
655+
let mut addr_nl: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
656+
addr_nl.nl_family = libc::AF_NETLINK as u16;
657+
addr_nl.nl_pid = 0x38deb915;
658+
addr_nl.nl_groups = 0xf229a8ea;
659+
660+
let ptr = &addr_nl as *const _ as *const MaybeUninit<u8>;
661+
let len = std::mem::size_of_val(&addr_nl).try_into().unwrap();
662+
663+
let addr = unsafe { SockaddrStorage::from_ptr(ptr, len) }.unwrap();
664+
let addr = addr.as_netlink().unwrap();
665+
666+
assert_eq!(addr.pid(), u32::from_le(addr_nl.nl_pid));
667+
assert_eq!(addr.groups(), u32::from_le(addr_nl.nl_groups));
668+
}
669+
670+
/// Convert from a `NetlinkAddr` to a `SockaddrStorage` to a `sockaddr_nl`.
671+
#[test]
672+
fn netlink_addr_to_libc() {
673+
let addr_original = nix::sys::socket::NetlinkAddr::new(0x38deb915, 0xf229a8ea);
674+
let addr = SockaddrStorage::from_netlink(&addr_original);
675+
676+
let (ptr, len) = addr.as_ptr();
677+
let ptr = ptr as *const libc::sockaddr_nl;
678+
assert_eq!(len as usize, std::mem::size_of::<libc::sockaddr_nl>());
679+
680+
let addr = unsafe { ptr.as_ref() }.unwrap();
681+
682+
assert_eq!(addr.nl_family, libc::AF_NETLINK as u16);
683+
assert_eq!(u32::from_le(addr.nl_pid), addr_original.pid());
684+
assert_eq!(u32::from_le(addr.nl_groups), addr_original.groups());
685+
}
686+
591687
/// Convert from a pathname `sockaddr_un` to a `SockaddrStorage` to a `SockaddrUnix`.
592688
#[test]
593689
fn unix_addr_from_libc_to_path() {

0 commit comments

Comments
 (0)