Skip to main content

netlink_socket2/
multicast.rs

1use std::{io, mem::MaybeUninit, os::fd::AsRawFd, sync::Arc};
2
3use netlink_bindings::utils;
4
5use super::{sock::NetlinkReplyInner, NetlinkSocket, Socket};
6#[allow(unused_imports)]
7use super::{Read, Write};
8use crate::{ReplyError, RECV_BUF_SIZE};
9
10#[derive(Debug, Clone)]
11pub struct MulticastRecv {
12    pub multicast_group: u32,
13    pub message_type: u16,
14}
15
16pub struct MulticastSocketRaw {
17    buf: Arc<[u8; RECV_BUF_SIZE]>,
18    sock: Socket,
19    reply: NetlinkReplyInner,
20    last_group: Option<u32>,
21}
22
23struct WrapSendUnsafe<T>(T);
24unsafe impl<T> Send for WrapSendUnsafe<T> {}
25
26type WrapMsghdr = WrapSendUnsafe<libc::msghdr>;
27
28impl MulticastSocketRaw {
29    pub fn new(protonum: u16) -> io::Result<Self> {
30        let sock = NetlinkSocket::get_socket_new(protonum)?;
31
32        // Enable multicast group number via recvmsg ancillary messages
33        let res = unsafe {
34            libc::setsockopt(
35                sock.as_raw_fd(),
36                libc::SOL_NETLINK,
37                libc::NETLINK_PKTINFO,
38                &1u32 as *const u32 as *const libc::c_void,
39                4,
40            )
41        };
42        if res < 0 {
43            return Err(io::Error::from_raw_os_error(-res));
44        }
45
46        let mut buf: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
47        buf.nl_family = libc::AF_NETLINK as u16;
48        buf.nl_groups = 0;
49
50        let res = unsafe {
51            libc::bind(
52                sock.as_raw_fd(),
53                &buf as *const _ as *const libc::sockaddr,
54                std::mem::size_of_val(&buf) as libc::socklen_t,
55            )
56        };
57        if res < 0 {
58            return Err(io::Error::last_os_error());
59        }
60
61        Ok(Self {
62            buf: Arc::new([0u8; RECV_BUF_SIZE]),
63            sock,
64            reply: NetlinkReplyInner {
65                buf_offset: 0,
66                buf_read: 0,
67            },
68            last_group: None,
69        })
70    }
71
72    pub fn listen(&mut self, group_id: u32) -> io::Result<()> {
73        let res = unsafe {
74            libc::setsockopt(
75                self.sock.as_raw_fd(),
76                libc::SOL_NETLINK,
77                libc::NETLINK_ADD_MEMBERSHIP,
78                &group_id as *const u32 as *const libc::c_void,
79                4,
80            )
81        };
82        if res < 0 {
83            return Err(io::Error::last_os_error());
84        }
85
86        Ok(())
87    }
88
89    #[super::strip_async]
90    pub async fn recv(&mut self) -> Result<(MulticastRecv, &[u8]), ReplyError> {
91        let buf = Arc::make_mut(&mut self.buf);
92
93        loop {
94            if self.reply.buf_offset == self.reply.buf_read {
95                let read = Self::read_buf(&self.sock, buf, &mut self.last_group).await?;
96                self.reply.buf_read = read;
97                self.reply.buf_offset = 0;
98            }
99
100            match self.reply.parse_next(buf).await {
101                Err(io_err) => {
102                    return Err(io_err.into());
103                }
104                Ok((_seq, message_type, res)) => {
105                    // Seq number is mostly, but not always, 0. For example, removing a network
106                    // interface on linux 6.1 creates a multicast notification with the same seq
107                    // number as in the request. We don't allow sending requests on the multicast
108                    // socket, so the only incoming messages here are notifications.
109
110                    let Some(multicast_group) = self.last_group else {
111                        continue;
112                    };
113
114                    match res {
115                        Ok((l, r)) => {
116                            return Ok((
117                                MulticastRecv {
118                                    multicast_group,
119                                    message_type,
120                                },
121                                &self.buf[l..r],
122                            ));
123                        }
124                        Err(mut err) => {
125                            if err.code.raw_os_error().unwrap() == 0 {
126                                continue;
127                            }
128
129                            if err.has_context() {
130                                // err.lookup = Request::lookup;
131                                err.reply_buf = Some(self.buf.clone());
132                            }
133
134                            return Err(err);
135                        }
136                    };
137                }
138            };
139        }
140    }
141
142    #[super::strip_async]
143    async fn read_buf(
144        sock: &Socket,
145        buf: &mut [u8],
146        last_group: &mut Option<u32>,
147    ) -> Result<usize, ReplyError> {
148        let mut iov = WrapSendUnsafe(libc::iovec {
149            iov_base: buf.as_mut_ptr() as *mut libc::c_void,
150            iov_len: buf.len(),
151        });
152
153        const CONTROL_BUF_LEN: usize = 128;
154        let mut control_buf = MaybeUninit::<[u8; CONTROL_BUF_LEN]>::uninit();
155
156        // Use zeroed init + field assignment to avoid private-field struct
157        // literal restrictions on musl targets (__pad1, __pad2 in msghdr).
158        let mut msghdr: libc::msghdr = unsafe { std::mem::zeroed() };
159        msghdr.msg_name = std::ptr::null_mut();
160        msghdr.msg_namelen = 0;
161        msghdr.msg_iov = &raw mut iov.0;
162        msghdr.msg_iovlen = 1;
163        msghdr.msg_control = control_buf.as_mut_ptr() as *mut libc::c_void;
164        // msg_controllen is usize on glibc but u32 on musl - let the compiler infer.
165        msghdr.msg_controllen = CONTROL_BUF_LEN as _;
166        msghdr.msg_flags = 0;
167
168        let mut msghdr = WrapSendUnsafe(msghdr);
169        let read = Self::recvmsg(&sock, &mut msghdr).await?;
170
171        *last_group = None;
172        unsafe {
173            let msghdr_ptr = &raw const msghdr.0;
174            let mut cmsg_ptr: *const libc::cmsghdr = libc::CMSG_FIRSTHDR(msghdr_ptr);
175            while !cmsg_ptr.is_null() {
176                let libc::cmsghdr {
177                        cmsg_len,
178                        cmsg_level,
179                        cmsg_type,
180                        .. // ignore __pad1 on musl
181                    } = *cmsg_ptr;
182
183                match (cmsg_level, cmsg_type) {
184                    (libc::SOL_NETLINK, libc::NETLINK_PKTINFO) => {
185                        let data = std::slice::from_raw_parts(
186                            libc::CMSG_DATA(cmsg_ptr),
187                            cmsg_len as usize - libc::CMSG_LEN(0) as usize,
188                        );
189                        *last_group = Some(utils::parse_u32(&data[..4]).unwrap());
190                    }
191                    _ => {}
192                }
193
194                cmsg_ptr = libc::CMSG_NXTHDR(msghdr_ptr, cmsg_ptr);
195            }
196        }
197
198        Ok(read)
199    }
200
201    fn recvmsg_sync(sock: &Socket, msghdr: &mut WrapMsghdr) -> io::Result<usize> {
202        unsafe {
203            let res = libc::recvmsg(sock.as_raw_fd(), &mut msghdr.0, 0);
204            if res < 0 {
205                return Err(io::Error::last_os_error());
206            }
207            Ok(res as usize)
208        }
209    }
210
211    #[super::only_sync]
212    fn recvmsg(sock: &Socket, msghdr: &mut WrapMsghdr) -> io::Result<usize> {
213        Self::recvmsg_sync(sock, msghdr)
214    }
215
216    #[super::only_async]
217    #[super::not_tokio]
218    async fn recvmsg(sock: &Socket, msghdr: &mut WrapMsghdr) -> io::Result<usize> {
219        loop {
220            match Self::recvmsg_sync(sock, msghdr) {
221                Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
222                    sock.readable().await?;
223                    continue;
224                }
225                res => return res,
226            }
227        }
228    }
229
230    #[super::only_async]
231    #[super::only_tokio]
232    async fn recvmsg(sock: &Socket, msghdr: &mut WrapMsghdr) -> io::Result<usize> {
233        // Tokio attempts to be clever and doesn't poll the fd if it hasn't seen EAGAIN first hand
234        sock.async_io(tokio::io::Interest::READABLE, || {
235            Self::recvmsg_sync(sock, msghdr)
236        })
237        .await
238    }
239}