1use crate as neli;
12
13use std::io::Cursor;
14
15use crate::{
16 attr::{AttrHandle, AttrHandleMut, Attribute},
17 consts::rtnl::*,
18 err::{DeError, SerError},
19 types::{Buffer, RtBuffer},
20 FromBytes, FromBytesWithInput, Header, Size, ToBytes,
21};
22
23#[derive(Debug, Size, ToBytes, FromBytesWithInput, Header)]
25pub struct Ifinfomsg {
26 pub ifi_family: RtAddrFamily,
28 padding: u8,
29 pub ifi_type: Arphrd,
31 pub ifi_index: libc::c_int,
33 pub ifi_flags: IffFlags,
35 pub ifi_change: IffFlags,
37 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?")]
39 pub rtattrs: RtBuffer<Ifla, Buffer>,
40}
41
42impl Ifinfomsg {
43 pub fn new(
45 ifi_family: RtAddrFamily,
46 ifi_type: Arphrd,
47 ifi_index: libc::c_int,
48 ifi_flags: IffFlags,
49 ifi_change: IffFlags,
50 rtattrs: RtBuffer<Ifla, Buffer>,
51 ) -> Self {
52 Ifinfomsg {
53 ifi_family,
54 padding: 0,
55 ifi_type,
56 ifi_index,
57 ifi_flags,
58 ifi_change,
59 rtattrs,
60 }
61 }
62
63 pub fn up(
66 ifi_family: RtAddrFamily,
67 ifi_type: Arphrd,
68 ifi_index: libc::c_int,
69 rtattrs: RtBuffer<Ifla, Buffer>,
70 ) -> Self {
71 Ifinfomsg {
72 ifi_family,
73 padding: 0,
74 ifi_type,
75 ifi_index,
76 ifi_flags: IffFlags::new(&[Iff::Up]),
77 ifi_change: IffFlags::new(&[Iff::Up]),
78 rtattrs,
79 }
80 }
81
82 pub fn down(
85 ifi_family: RtAddrFamily,
86 ifi_type: Arphrd,
87 ifi_index: libc::c_int,
88 rtattrs: RtBuffer<Ifla, Buffer>,
89 ) -> Self {
90 Ifinfomsg {
91 ifi_family,
92 padding: 0,
93 ifi_type,
94 ifi_index,
95 ifi_flags: IffFlags::empty(),
96 ifi_change: IffFlags::new(&[Iff::Up]),
97 rtattrs,
98 }
99 }
100}
101
102#[derive(Debug, Size, ToBytes, FromBytesWithInput, Header)]
104pub struct Ifaddrmsg {
105 pub ifa_family: RtAddrFamily,
107 pub ifa_prefixlen: libc::c_uchar,
109 pub ifa_flags: IfaFFlags,
111 pub ifa_scope: libc::c_uchar,
113 pub ifa_index: libc::c_int,
115 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?")]
117 pub rtattrs: RtBuffer<Ifa, Buffer>,
118}
119
120#[derive(Debug, Size, ToBytes, FromBytes)]
123pub struct Rtgenmsg {
124 pub rtgen_family: RtAddrFamily,
126}
127
128#[derive(Debug, Size, ToBytes, FromBytesWithInput, Header)]
130pub struct Rtmsg {
131 pub rtm_family: RtAddrFamily,
133 pub rtm_dst_len: libc::c_uchar,
135 pub rtm_src_len: libc::c_uchar,
137 pub rtm_tos: libc::c_uchar,
139 pub rtm_table: RtTable,
141 pub rtm_protocol: Rtprot,
143 pub rtm_scope: RtScope,
145 pub rtm_type: Rtn,
147 pub rtm_flags: RtmFFlags,
149 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?")]
151 pub rtattrs: RtBuffer<Rta, Buffer>,
152}
153
154#[derive(Debug, Size, ToBytes, FromBytesWithInput, Header)]
156pub struct Ndmsg {
157 pub ndm_family: RtAddrFamily,
159 pad1: u8,
160 pad2: u16,
161 pub ndm_index: libc::c_int,
163 pub ndm_state: NudFlags,
165 pub ndm_flags: NtfFlags,
167 pub ndm_type: Rtn,
169 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?")]
171 pub rtattrs: RtBuffer<Nda, Buffer>,
172}
173
174impl Ndmsg {
175 pub fn new(
177 ndm_family: RtAddrFamily,
178 ndm_index: libc::c_int,
179 ndm_state: NudFlags,
180 ndm_flags: NtfFlags,
181 ndm_type: Rtn,
182 rtattrs: RtBuffer<Nda, Buffer>,
183 ) -> Self {
184 Ndmsg {
185 ndm_family,
186 pad1: 0,
187 pad2: 0,
188 ndm_index,
189 ndm_state,
190 ndm_flags,
191 ndm_type,
192 rtattrs,
193 }
194 }
195}
196
197#[derive(Debug, Size, ToBytes, FromBytes)]
199pub struct NdaCacheinfo {
200 pub ndm_confirmed: u32,
202 pub ndm_used: u32,
204 pub ndm_updated: u32,
206 pub ndm_refcnt: u32,
208}
209
210#[derive(Debug, Size, ToBytes, FromBytesWithInput, Header)]
212pub struct Tcmsg {
213 pub tcm_family: libc::c_uchar,
215 padding_char: libc::c_uchar,
216 padding_short: libc::c_ushort,
217 pub tcm_ifindex: libc::c_int,
219 pub tcm_handle: u32,
221 pub tcm_parent: u32,
223 pub tcm_info: u32,
225 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?")]
227 pub rtattrs: RtBuffer<Tca, Buffer>,
228}
229
230impl Tcmsg {
231 pub fn new(
234 tcm_family: libc::c_uchar,
235 tcm_ifindex: libc::c_int,
236 tcm_handle: u32,
237 tcm_parent: u32,
238 tcm_info: u32,
239 rtattrs: RtBuffer<Tca, Buffer>,
240 ) -> Self {
241 Tcmsg {
242 tcm_family,
243 padding_char: 0,
244 padding_short: 0,
245 tcm_ifindex,
246 tcm_handle,
247 tcm_parent,
248 tcm_info,
249 rtattrs,
250 }
251 }
252}
253
254#[derive(Debug, Size, ToBytes, FromBytes, Header)]
256#[neli(header_bound = "T: RtaType")]
257#[neli(from_bytes_bound = "T: RtaType")]
258#[neli(from_bytes_bound = "P: FromBytesWithInput<Input = usize>")]
259#[neli(padding)]
260pub struct Rtattr<T, P> {
261 pub rta_len: libc::c_ushort,
263 pub rta_type: T,
265 #[neli(
267 input = "(rta_len as usize).checked_sub(Self::header_size()).ok_or(DeError::UnexpectedEOB)?"
268 )]
269 pub rta_payload: P,
270}
271
272impl<T> Rtattr<T, Buffer>
273where
274 T: RtaType,
275{
276 pub fn new<P>(_: Option<u16>, rta_type: T, rta_payload: P) -> Result<Self, SerError>
278 where
279 P: Size + ToBytes,
280 {
281 let mut attr = Rtattr {
282 rta_len: Self::header_size() as u16,
283 rta_type,
284 rta_payload: Buffer::new(),
285 };
286 attr.set_payload(&rta_payload)?;
287 Ok(attr)
288 }
289
290 pub fn add_nested_attribute<TT, P>(&mut self, attr: &Rtattr<TT, P>) -> Result<(), SerError>
292 where
293 TT: RtaType,
294 P: ToBytes,
295 {
296 let mut buffer = Cursor::new(Vec::new());
297 attr.to_bytes(&mut buffer)?;
298
299 self.rta_payload.extend_from_slice(buffer.get_ref());
300 self.rta_len += buffer.get_ref().len() as u16;
301 Ok(())
302 }
303
304 pub fn get_attr_handle<R>(&self) -> Result<RtAttrHandle<R>, DeError>
307 where
308 R: RtaType,
309 {
310 Ok(AttrHandle::new(RtBuffer::from_bytes_with_input(
311 &mut Cursor::new(self.rta_payload.as_ref()),
312 self.rta_payload.len(),
313 )?))
314 }
315
316 pub fn get_attr_handle_mut<R>(&mut self) -> Result<RtAttrHandleMut<R>, DeError>
319 where
320 R: RtaType,
321 {
322 Ok(AttrHandleMut::new(RtBuffer::from_bytes_with_input(
323 &mut Cursor::new(self.rta_payload.as_ref()),
324 self.rta_payload.len(),
325 )?))
326 }
327}
328
329impl<T> Attribute<T> for Rtattr<T, Buffer>
330where
331 T: RtaType,
332{
333 fn payload(&self) -> &Buffer {
334 &self.rta_payload
335 }
336
337 fn set_payload<P>(&mut self, payload: &P) -> Result<(), SerError>
338 where
339 P: Size + ToBytes,
340 {
341 let mut buffer = Cursor::new(Vec::new());
342 payload.to_bytes(&mut buffer)?;
343
344 self.rta_len -= self.rta_payload.unpadded_size() as u16;
346 self.rta_len += buffer.get_ref().len() as u16;
347
348 self.rta_payload = Buffer::from(buffer.into_inner());
349
350 Ok(())
351 }
352}
353
354type RtAttrHandle<'a, T> = AttrHandle<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>;
355type RtAttrHandleMut<'a, T> = AttrHandleMut<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>;
356
357impl<'a, T> AttrHandle<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>
358where
359 T: RtaType,
360{
361 pub fn get_nested_attributes<S>(&mut self, subattr: T) -> Result<RtAttrHandle<S>, DeError>
364 where
365 S: RtaType,
366 {
367 let payload = self
368 .get_attribute(subattr)
369 .ok_or_else(|| DeError::new("Couldn't find specified attribute"))?
370 .rta_payload
371 .as_ref();
372 Ok(AttrHandle::new(RtBuffer::from_bytes_with_input(
373 &mut Cursor::new(payload),
374 payload.len(),
375 )?))
376 }
377
378 pub fn get_attribute(&self, t: T) -> Option<&Rtattr<T, Buffer>> {
380 self.get_attrs().iter().find(|item| item.rta_type == t)
381 }
382
383 pub fn get_attr_payload_as<'b, R>(&'b self, attr: T) -> Result<R, DeError>
385 where
386 R: FromBytes<'b>,
387 {
388 match self.get_attribute(attr) {
389 Some(a) => a.get_payload_as::<R>(),
390 _ => Err(DeError::new("Failed to find specified attribute")),
391 }
392 }
393
394 pub fn get_attr_payload_as_with_len<'b, R>(&'b self, attr: T) -> Result<R, DeError>
396 where
397 R: FromBytesWithInput<'b, Input = usize>,
398 {
399 match self.get_attribute(attr) {
400 Some(a) => a.get_payload_as_with_len::<R>(),
401 _ => Err(DeError::new("Failed to find specified attribute")),
402 }
403 }
404}
405
406#[cfg(test)]
407mod test {
408 use super::*;
409
410 use crate::{
411 consts::{
412 nl::{NlmF, NlmFFlags},
413 socket::NlFamily,
414 },
415 nl::{NlPayload, Nlmsghdr},
416 socket::NlSocketHandle,
417 test::setup,
418 };
419
420 #[test]
421 fn test_rta_deserialize() {
422 setup();
423
424 let buf = &[4u8, 0, 0, 0] as &[u8];
425 Rtattr::<Rta, Buffer>::from_bytes(&mut Cursor::new(buf)).unwrap();
426 }
427
428 #[test]
429 fn test_rta_deserialize_err() {
430 setup();
431
432 let buf = &[3u8, 0, 0, 0] as &[u8];
434 Rtattr::<Rta, Buffer>::from_bytes(&mut Cursor::new(buf)).unwrap_err();
435 }
436
437 #[test]
438 fn test_rtattr_padding() {
439 setup();
440
441 let attr = Rtattr {
442 rta_len: 5,
443 rta_type: Rta::Unspec,
444 rta_payload: vec![0u8],
445 };
446 let mut buffer = Cursor::new(Vec::new());
447 let buf_res = attr.to_bytes(&mut buffer);
448
449 buf_res.unwrap();
450 assert_eq!(buffer.into_inner().len(), 8);
452 }
453
454 #[test]
455 fn real_test_ifinfomsg() {
456 setup();
457
458 let mut sock = NlSocketHandle::new(NlFamily::Route).unwrap();
459 sock.send(Nlmsghdr::new(
460 None,
461 Rtm::Getlink,
462 NlmFFlags::new(&[NlmF::Dump, NlmF::Request, NlmF::Ack]),
463 None,
464 None,
465 NlPayload::Payload(Ifinfomsg::new(
466 RtAddrFamily::Unspecified,
467 Arphrd::None,
468 0,
469 IffFlags::empty(),
470 IffFlags::empty(),
471 RtBuffer::new(),
472 )),
473 ))
474 .unwrap();
475 let msgs = sock.recv_all::<Rtm, Ifinfomsg>().unwrap();
476 for msg in msgs {
477 let handle = msg.get_payload().unwrap().rtattrs.get_attr_handle();
478 handle
479 .get_attr_payload_as_with_len::<String>(Ifla::Ifname)
480 .unwrap();
481 assert_eq!(
483 handle
484 .get_attr_payload_as_with_len::<Vec<u8>>(Ifla::Address)
485 .unwrap()
486 .len(),
487 6
488 );
489 }
490 }
491
492 #[test]
493 fn real_test_tcmsg() {
494 setup();
495
496 let mut sock = NlSocketHandle::new(NlFamily::Route).unwrap();
497 sock.send(Nlmsghdr::new(
498 None,
499 Rtm::Getqdisc,
500 NlmFFlags::new(&[NlmF::Dump, NlmF::Request, NlmF::Ack]),
501 None,
502 None,
503 NlPayload::Payload(Tcmsg::new(0, 0, 0, 0, 0, RtBuffer::new())),
504 ))
505 .unwrap();
506 let msgs = sock.recv_all::<Rtm, Tcmsg>().unwrap();
507 for msg in msgs {
508 assert!(matches!(msg.get_payload().unwrap(), Tcmsg { .. }));
509 assert_eq!(msg.nl_type, Rtm::Newqdisc);
510 }
511 }
512}