netlink_socket2/
multicast.rs1use std::{io, os::fd::AsRawFd, sync::Arc};
2
3use netlink_bindings::utils;
4
5use crate::{NetlinkReplyInner, NetlinkSocket, ReplyError, Socket, RECV_BUF_SIZE};
6
7#[derive(Debug, Clone)]
8pub struct MulticastRecv {
9 pub multicast_group: u32,
10 pub message_type: u16,
11}
12
13pub struct MulticastSocketRaw {
14 buf: Arc<[u8; RECV_BUF_SIZE]>,
15 sock: Socket,
16 reply: NetlinkReplyInner,
17 last_group: Option<u32>,
18}
19
20impl MulticastSocketRaw {
21 pub fn new(protonum: u16) -> io::Result<Self> {
22 let sock = NetlinkSocket::get_socket_new(protonum)?;
23
24 let res = unsafe {
26 libc::setsockopt(
27 sock.as_raw_fd(),
28 libc::SOL_NETLINK,
29 libc::NETLINK_PKTINFO,
30 &1u32 as *const u32 as *const libc::c_void,
31 4,
32 )
33 };
34 if res < 0 {
35 return Err(io::Error::from_raw_os_error(-res));
36 }
37
38 let mut buf: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
39 buf.nl_family = libc::AF_NETLINK as u16;
40 buf.nl_groups = 0;
41
42 let res = unsafe {
43 libc::bind(
44 sock.as_raw_fd(),
45 &buf as *const _ as *const libc::sockaddr,
46 std::mem::size_of_val(&buf) as libc::socklen_t,
47 )
48 };
49 if res < 0 {
50 return Err(io::Error::last_os_error());
51 }
52
53 Ok(Self {
54 buf: Arc::new([0u8; RECV_BUF_SIZE]),
55 sock,
56 reply: NetlinkReplyInner {
57 buf_offset: 0,
58 buf_read: 0,
59 },
60 last_group: None,
61 })
62 }
63
64 pub fn listen(&mut self, group_id: u32) -> io::Result<()> {
65 let res = unsafe {
66 libc::setsockopt(
67 self.sock.as_raw_fd(),
68 libc::SOL_NETLINK,
69 libc::NETLINK_ADD_MEMBERSHIP,
70 &group_id as *const u32 as *const libc::c_void,
71 4,
72 )
73 };
74 if res < 0 {
75 return Err(io::Error::last_os_error());
76 }
77
78 Ok(())
79 }
80
81 #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
82 pub async fn recv(&mut self) -> Result<(MulticastRecv, &[u8]), ReplyError> {
83 let buf = Arc::make_mut(&mut self.buf);
84
85 loop {
86 if self.reply.buf_offset == self.reply.buf_read {
87 let read = Self::read_buf(&self.sock, buf, &mut self.last_group).await?;
88 self.reply.buf_read = read;
89 self.reply.buf_offset = 0;
90 }
91
92 match self.reply.parse_next(buf).await {
93 Err(io_err) => {
94 return Err(io_err.into());
95 }
96 Ok((_seq, message_type, res)) => {
97 let Some(multicast_group) = self.last_group else {
103 continue;
104 };
105
106 match res {
107 Ok((l, r)) => {
108 return Ok((
109 MulticastRecv {
110 multicast_group,
111 message_type,
112 },
113 &self.buf[l..r],
114 ));
115 }
116 Err(mut err) => {
117 if err.code.raw_os_error().unwrap() == 0 {
118 continue;
119 }
120
121 if err.has_context() {
122 err.reply_buf = Some(self.buf.clone());
124 }
125
126 return Err(err);
127 }
128 };
129 }
130 };
131 }
132 }
133
134 #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)]
135 async fn read_buf(
136 sock: &Socket,
137 buf: &mut [u8],
138 last_group: &mut Option<u32>,
139 ) -> Result<usize, ReplyError> {
140 loop {
141 let mut addr: libc::sockaddr_nl = unsafe { std::mem::zeroed() };
142 let mut iov = libc::iovec {
143 iov_base: buf.as_mut_ptr() as *mut libc::c_void,
144 iov_len: buf.len(),
145 };
146
147 let mut control_buf = [0u8; 128];
148
149 let mut msghdr: libc::msghdr = unsafe { std::mem::zeroed() };
152 msghdr.msg_name = &mut addr as *mut libc::sockaddr_nl as *mut libc::c_void;
153 msghdr.msg_namelen = std::mem::size_of_val(&addr) as u32;
154 msghdr.msg_iov = &mut iov as *mut libc::iovec;
155 msghdr.msg_iovlen = 1;
156 msghdr.msg_control = control_buf.as_mut_ptr() as *mut libc::c_void;
157 msghdr.msg_controllen = control_buf.len() as _;
159 msghdr.msg_flags = 0;
160
161 let do_recvmsg = || unsafe {
162 let res = libc::recvmsg(sock.as_raw_fd(), &mut msghdr, 0);
163 if res < 0 {
164 return Err(io::Error::last_os_error());
165 }
166 Ok(res)
167 };
168
169 #[cfg(feature = "tokio")]
171 let read = sock
172 .async_io(tokio::io::Interest::READABLE, do_recvmsg)
173 .await?;
174
175 #[cfg(not(feature = "tokio"))]
176 let read = match { do_recvmsg }() {
177 #[cfg(feature = "async")]
178 Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
179 sock.readable().await?;
180 continue;
181 }
182 Err(err) => return Err(err.into()),
183 Ok(read) => read,
184 };
185
186 *last_group = None;
187 unsafe {
188 let msghdr_ptr = &msghdr as *const libc::msghdr;
189 let mut cmsg_ptr: *const libc::cmsghdr = libc::CMSG_FIRSTHDR(msghdr_ptr);
190 while !cmsg_ptr.is_null() {
191 let libc::cmsghdr {
192 cmsg_len,
193 cmsg_level,
194 cmsg_type,
195 .. } = *cmsg_ptr;
197
198 match (cmsg_level, cmsg_type) {
199 (libc::SOL_NETLINK, libc::NETLINK_PKTINFO) => {
200 let data = std::slice::from_raw_parts(
201 libc::CMSG_DATA(cmsg_ptr),
202 cmsg_len as usize - libc::CMSG_LEN(0) as usize,
203 );
204 *last_group = Some(utils::parse_u32(&data[..4]).unwrap());
205 }
206 _ => {}
207 }
208
209 cmsg_ptr = libc::CMSG_NXTHDR(msghdr_ptr, cmsg_ptr);
210 }
211 }
212
213 return Ok(read as usize);
214 }
215 }
216}