1use std::{
2 collections::{hash_map::Entry, HashMap},
3 io::{self, ErrorKind, IoSlice},
4 marker::PhantomData,
5 os::fd::{AsRawFd, FromRawFd, OwnedFd},
6 sync::Arc,
7};
8
9#[allow(unused_imports)]
10use super::{Read, Socket, Write};
11use crate::{ReplyError, RECV_BUF_SIZE};
12
13use netlink_bindings::{
14 builtin::Nlmsghdr,
15 nlctrl,
16 traits::{NetlinkRequest, Protocol},
17 utils,
18};
19
20pub struct NetlinkSocket {
21 pub(crate) buf: Arc<[u8; RECV_BUF_SIZE]>,
22 pub(crate) cache: HashMap<&'static [u8], u16>,
23 pub(crate) sock: HashMap<u16, Socket>,
24 pub(crate) seq: u32,
25}
26
27impl Default for NetlinkSocket {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl NetlinkSocket {
34 pub fn new() -> Self {
35 Self {
36 buf: Arc::new([0u8; RECV_BUF_SIZE]),
37 cache: HashMap::default(),
38 sock: HashMap::new(),
39 seq: 1,
40 }
41 }
42
43 pub(crate) fn get_socket_cached(
44 cache: &mut HashMap<u16, Socket>,
45 protonum: u16,
46 ) -> io::Result<&mut Socket> {
47 match cache.entry(protonum) {
48 Entry::Occupied(sock) => Ok(sock.into_mut()),
49 Entry::Vacant(ent) => {
50 let sock = Self::get_socket_new(protonum)?;
51 Ok(ent.insert(sock))
52 }
53 }
54 }
55
56 pub(crate) fn get_socket_new(family: u16) -> io::Result<Socket> {
57 let fd = unsafe {
58 libc::socket(
59 libc::AF_NETLINK,
60 libc::SOCK_RAW | libc::SOCK_CLOEXEC,
61 family as i32,
62 )
63 };
64 if fd < 0 {
65 return Err(io::Error::from_raw_os_error(-fd));
66 }
67 let fd = unsafe { OwnedFd::from_raw_fd(fd) };
68
69 let res = unsafe {
71 libc::setsockopt(
72 fd.as_raw_fd(),
73 libc::SOL_NETLINK,
74 libc::NETLINK_EXT_ACK,
75 &1u32 as *const u32 as *const libc::c_void,
76 4,
77 )
78 };
79 if res < 0 {
80 return Err(io::Error::from_raw_os_error(-res));
81 }
82
83 Self::convert_sock(fd.into())
84 }
85
86 #[super::only_async]
87 fn convert_sock(sock: std::net::TcpStream) -> io::Result<Socket> {
88 sock.set_nonblocking(true)?;
89 Socket::try_from(sock)
90 }
91
92 #[super::only_sync]
93 fn convert_sock(sock: std::net::TcpStream) -> io::Result<Socket> {
94 Ok(sock)
95 }
96
97 pub fn reserve_seq(&mut self, len: u32) -> u32 {
100 let seq = self.seq;
101 self.seq = self.seq.wrapping_add(len);
102 seq
103 }
104
105 #[super::strip_async]
106 pub async fn request<'sock, Request>(
107 &'sock mut self,
108 request: &Request,
109 ) -> io::Result<NetlinkReply<'sock, Request>>
110 where
111 Request: NetlinkRequest,
112 {
113 let (protonum, request_type) = match request.protocol() {
114 Protocol::Raw {
115 protonum,
116 request_type,
117 } => (protonum, request_type),
118 Protocol::Generic(name) => (libc::GENL_ID_CTRL as u16, self.resolve(name).await?),
119 };
120
121 self.request_raw(request, protonum, request_type).await
122 }
123
124 #[super::strip_async]
125 async fn resolve(&mut self, family_name: &'static [u8]) -> io::Result<u16> {
126 if let Some(id) = self.cache.get(family_name) {
127 return Ok(*id);
128 }
129
130 let mut request = nlctrl::Request::new().op_getfamily_do();
131 request.encode().push_family_name_bytes(family_name);
132
133 let Protocol::Raw {
134 protonum,
135 request_type,
136 } = request.protocol()
137 else {
138 unreachable!()
139 };
140 assert_eq!(protonum, libc::NETLINK_GENERIC as u16);
141 assert_eq!(request_type, libc::GENL_ID_CTRL as u16);
142
143 let mut iter = self.request_raw(&request, protonum, request_type).await?;
144 if let Some(reply) = iter.recv().await {
145 let Ok(id) = reply?.get_family_id() else {
146 return Err(ErrorKind::Unsupported.into());
147 };
148 self.cache.insert(family_name, id);
149 return Ok(id);
150 }
151
152 Err(ErrorKind::UnexpectedEof.into())
153 }
154
155 #[super::strip_async]
156 async fn request_raw<'sock, Request>(
157 &'sock mut self,
158 request: &Request,
159 protonum: u16,
160 request_type: u16,
161 ) -> io::Result<NetlinkReply<'sock, Request>>
162 where
163 Request: NetlinkRequest,
164 {
165 let seq = self.reserve_seq(1);
166 let sock = Self::get_socket_cached(&mut self.sock, protonum)?;
167
168 let header = Nlmsghdr {
169 len: Nlmsghdr::len() as u32 + request.payload().len() as u32,
170 r#type: request_type,
171 flags: request.flags() | libc::NLM_F_REQUEST as u16 | libc::NLM_F_ACK as u16,
172 seq,
173 pid: 0,
174 };
175
176 Self::write_buf(
177 sock,
178 &[
179 IoSlice::new(header.as_slice()),
180 IoSlice::new(request.payload()),
181 ],
182 )
183 .await?;
184
185 Ok(NetlinkReply {
186 sock,
187 buf: &mut self.buf,
188 inner: NetlinkReplyInner {
189 buf_offset: 0,
190 buf_read: 0,
191 },
192 seq: header.seq,
193 done: false,
194 phantom: PhantomData,
195 })
196 }
197
198 #[super::not_tokio]
199 #[super::strip_async]
200 pub(crate) async fn write_buf(sock: &mut Socket, payload: &[IoSlice<'_>]) -> io::Result<()> {
201 loop {
202 match sock.write_vectored(payload).await {
203 Ok(sent) if sent != payload.iter().map(|s| s.len()).sum() => {
204 return Err(io::Error::other("Couldn't send the whole message"));
205 }
206 Ok(_) => return Ok(()),
207 Err(err) if err.kind() == ErrorKind::Interrupted => continue,
208 Err(err) => return Err(err),
209 }
210 }
211 }
212
213 #[super::only_tokio]
214 #[super::strip_async]
215 pub(crate) async fn write_buf(sock: &mut Socket, payload: &[IoSlice<'_>]) -> io::Result<()> {
216 loop {
217 let res = sock.try_write_vectored(payload);
221 if matches!(&res, Err(err) if err.kind() == ErrorKind::WouldBlock) {
222 sock.writable().await?;
223 continue;
224 }
225
226 match res {
227 Ok(sent) if sent != payload.iter().map(|s| s.len()).sum() => {
228 return Err(io::Error::other("Couldn't send the whole message"));
229 }
230 Ok(_) => return Ok(()),
231 Err(err) if err.kind() == ErrorKind::Interrupted => continue,
232 Err(err) => return Err(err),
233 }
234 }
235 }
236}
237
238pub(crate) struct NetlinkReplyInner {
239 pub(crate) buf_offset: usize,
240 pub(crate) buf_read: usize,
241}
242
243impl NetlinkReplyInner {
244 #[super::not_tokio]
245 #[super::strip_async]
246 async fn read_buf(sock: &mut Socket, buf: &mut [u8]) -> io::Result<usize> {
247 loop {
248 match sock.read(&mut buf[..]).await {
249 Ok(read) => return Ok(read),
250 Err(err) if err.kind() == ErrorKind::Interrupted => continue,
251 Err(err) => return Err(err),
252 }
253 }
254 }
255
256 #[super::only_tokio]
257 #[super::strip_async]
258 async fn read_buf(sock: &mut Socket, buf: &mut [u8]) -> io::Result<usize> {
259 loop {
260 let res = sock.try_read(&mut buf[..]);
264 if matches!(&res, Err(err) if err.kind() == ErrorKind::WouldBlock) {
265 sock.readable().await?;
266 continue;
267 }
268
269 match res {
270 Ok(read) => return Ok(read),
271 Err(err) if err.kind() == ErrorKind::Interrupted => continue,
272 Err(err) => return Err(err),
273 }
274 }
275 }
276
277 #[allow(clippy::type_complexity)]
278 #[super::strip_async]
279 pub async fn recv(
280 &mut self,
281 sock: &mut Socket,
282 buf: &mut [u8; RECV_BUF_SIZE],
283 ) -> io::Result<(u32, u16, Result<(usize, usize), ReplyError>)> {
284 if self.buf_offset == self.buf_read {
285 self.buf_read = Self::read_buf(sock, &mut buf[..]).await?;
286 self.buf_offset = 0;
287 }
288 self.parse_next(buf).await
289 }
290
291 #[allow(clippy::type_complexity)]
292 #[super::strip_async]
293 pub(crate) async fn parse_next(
294 &mut self,
295 buf: &[u8; RECV_BUF_SIZE],
296 ) -> io::Result<(u32, u16, Result<(usize, usize), ReplyError>)> {
297 let packet = &buf[self.buf_offset..self.buf_read];
298
299 let too_short_err = || io::Error::other("Received packet is too short");
300
301 let Some(header) = packet.get(..Nlmsghdr::len()) else {
302 return Err(too_short_err());
303 };
304 let header = Nlmsghdr::from_slice(header);
305
306 let payload_start = self.buf_offset + Nlmsghdr::len();
307 self.buf_offset += header.len as usize;
308
309 match header.r#type as i32 {
310 libc::NLMSG_DONE | libc::NLMSG_ERROR => {
311 let Some(code) = packet.get(16..20) else {
312 return Err(too_short_err());
313 };
314 let code = utils::parse_i32(code).unwrap();
315
316 let (echo_start, echo_end) =
317 if code == 0 || header.r#type == libc::NLMSG_DONE as u16 {
318 (20, 20)
319 } else {
320 let Some(echo_header) = packet.get(20..(20 + Nlmsghdr::len())) else {
321 return Err(too_short_err());
322 };
323 let echo_header = Nlmsghdr::from_slice(echo_header);
324
325 if echo_header.flags & libc::NLM_F_CAPPED as u16 == 0 {
326 let start = echo_header.len;
327 if packet.len() < start as usize + 20 {
328 return Err(too_short_err());
329 }
330
331 (20 + 16, 20 + start as usize)
332 } else {
333 let ext_ack_start = 20 + Nlmsghdr::len();
334 (ext_ack_start, ext_ack_start)
335 }
336 };
337
338 let ext_ack_start =
339 utils::align_up(echo_end, utils::NLA_ALIGNTO).min(self.buf_offset);
340
341 Ok((
342 header.seq,
343 header.r#type,
344 Err(ReplyError {
345 code: io::Error::from_raw_os_error(-code),
346 request_bounds: (echo_start as u32, echo_end as u32),
347 ext_ack_bounds: (ext_ack_start as u32, self.buf_offset as u32),
348 reply_buf: None,
349 chained_name: None,
350 lookup: None,
351 }),
352 ))
353 }
354 libc::NLMSG_NOOP => Ok((
355 header.seq,
356 header.r#type,
357 Err(io::Error::other("Received NLMSG_NOOP").into()),
358 )),
359 libc::NLMSG_OVERRUN => Ok((
360 header.seq,
361 header.r#type,
362 Err(io::Error::other("Received NLMSG_OVERRUN").into()),
363 )),
364 _ => Ok((
365 header.seq,
366 header.r#type,
367 Ok((payload_start, self.buf_offset)),
368 )),
369 }
370 }
371}
372
373pub struct NetlinkReply<'sock, Request: NetlinkRequest> {
374 inner: NetlinkReplyInner,
375 sock: &'sock mut Socket,
376 buf: &'sock mut Arc<[u8; RECV_BUF_SIZE]>,
377 seq: u32,
378 done: bool,
379 phantom: PhantomData<Request>,
380}
381
382impl<Request: NetlinkRequest> NetlinkReply<'_, Request> {
383 #[super::strip_async]
384 pub async fn recv_one(&mut self) -> Result<Request::ReplyType<'_>, ReplyError> {
385 if let Some(res) = self.recv().await {
386 return res;
387 }
388 Err(io::Error::other("Reply didn't contain data").into())
389 }
390
391 #[super::strip_async]
392 pub async fn recv_ack(&mut self) -> Result<(), ReplyError> {
393 if let Some(res) = self.recv().await {
394 res?;
395 return Err(io::Error::other("Reply isn't just an ack").into());
396 }
397 Ok(())
398 }
399
400 #[super::strip_async]
401 pub async fn recv(&mut self) -> Option<Result<Request::ReplyType<'_>, ReplyError>> {
402 if self.done {
403 return None;
404 }
405
406 let buf = Arc::make_mut(self.buf);
407
408 loop {
409 match self.inner.recv(self.sock, buf).await {
410 Err(io_err) => {
411 self.done = true;
412 return Some(Err(io_err.into()));
413 }
414 Ok((seq, _type, res)) => {
415 if seq != self.seq {
416 continue;
417 }
418 return match res {
419 Ok((l, r)) => Some(Ok(Request::decode_reply(&self.buf[l..r]))),
420 Err(mut err) => {
421 self.done = true;
422 if err.code.raw_os_error().unwrap() == 0 {
423 None
424 } else {
425 if err.has_context() {
426 err.lookup = Some(Request::lookup);
427 err.reply_buf = Some(self.buf.clone());
428 }
429 Some(Err(err))
430 }
431 }
432 };
433 }
434 };
435 }
436 }
437}
438
439#[cfg(test)]
440mod test {
441 use super::*;
442
443 #[allow(unused)]
444 trait SpawnCompatible: Send {}
445 impl<'a> SpawnCompatible for NetlinkSocket {}
446}