1use std::io::Cursor;
12
13use derive_builder::{Builder, UninitializedFieldError};
14use getset::Getters;
15
16use crate::{
17 self as neli, FromBytes, FromBytesWithInput, FromBytesWithInputBorrowed, Header, Size, ToBytes,
18 attr::{AttrHandle, Attribute},
19 consts::rtnl::*,
20 err::{DeError, SerError},
21 types::{Buffer, RtBuffer},
22};
23
24#[derive(Builder, Getters, Clone, Debug, Size, ToBytes, FromBytesWithInput, Header)]
26#[builder(pattern = "owned")]
27pub struct Ifinfomsg {
28 #[getset(get = "pub")]
30 ifi_family: RtAddrFamily,
31 #[builder(setter(skip))]
32 #[builder(default = "0")]
33 padding: u8,
34 #[getset(get = "pub")]
36 #[builder(default = "Arphrd::from(0)")]
37 ifi_type: Arphrd,
38 #[getset(get = "pub")]
40 #[builder(default = "0")]
41 ifi_index: libc::c_int,
42 #[getset(get = "pub")]
44 #[builder(default = "Iff::empty()")]
45 ifi_flags: Iff,
46 #[getset(get = "pub")]
48 #[builder(default = "Iff::empty()")]
49 ifi_change: Iff,
50 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
52 #[getset(get = "pub")]
53 #[builder(default = "RtBuffer::new()")]
54 rtattrs: RtBuffer<Ifla, Buffer>,
55}
56
57impl IfinfomsgBuilder {
58 pub fn up(mut self) -> Self {
61 self.ifi_flags = Some(self.ifi_flags.unwrap_or_else(Iff::empty) | Iff::UP);
62 self.ifi_change = Some(self.ifi_change.unwrap_or_else(Iff::empty) | Iff::UP);
63 self
64 }
65
66 pub fn down(mut self) -> Self {
69 self.ifi_flags = Some(self.ifi_flags.unwrap_or_else(Iff::empty) & !Iff::UP);
70 self.ifi_change = Some(self.ifi_change.unwrap_or_else(Iff::empty) | Iff::UP);
71 self
72 }
73}
74
75#[derive(Builder, Getters, Clone, Debug, Size, ToBytes, FromBytesWithInput, Header)]
77#[builder(pattern = "owned")]
78pub struct Ifaddrmsg {
79 #[getset(get = "pub")]
81 ifa_family: RtAddrFamily,
82 #[getset(get = "pub")]
84 ifa_prefixlen: libc::c_uchar,
85 #[getset(get = "pub")]
87 #[builder(default = "IfaF::empty()")]
88 ifa_flags: IfaF,
89 #[getset(get = "pub")]
91 ifa_scope: RtScope,
92 #[getset(get = "pub")]
94 ifa_index: libc::c_uint,
95 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
97 #[getset(get = "pub")]
98 #[builder(default = "RtBuffer::new()")]
99 rtattrs: RtBuffer<Ifa, Buffer>,
100}
101
102#[derive(Builder, Getters, Debug, Size, ToBytes, FromBytesWithInput, Header)]
105#[builder(pattern = "owned")]
106pub struct Rtgenmsg {
107 #[getset(get = "pub")]
109 rtgen_family: RtAddrFamily,
110 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
112 #[getset(get = "pub")]
113 #[builder(default = "RtBuffer::new()")]
114 rtattrs: RtBuffer<Ifa, Buffer>,
115}
116
117#[derive(Builder, Getters, Clone, Debug, Size, ToBytes, FromBytesWithInput, Header)]
119#[builder(pattern = "owned")]
120pub struct Rtmsg {
121 #[getset(get = "pub")]
123 rtm_family: RtAddrFamily,
124 #[getset(get = "pub")]
126 rtm_dst_len: libc::c_uchar,
127 #[getset(get = "pub")]
129 rtm_src_len: libc::c_uchar,
130 #[getset(get = "pub")]
132 rtm_tos: libc::c_uchar,
133 #[getset(get = "pub")]
135 rtm_table: RtTable,
136 #[getset(get = "pub")]
138 rtm_protocol: Rtprot,
139 #[getset(get = "pub")]
141 rtm_scope: RtScope,
142 #[getset(get = "pub")]
144 rtm_type: Rtn,
145 #[builder(default = "RtmF::empty()")]
147 #[getset(get = "pub")]
148 rtm_flags: RtmF,
149 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
151 #[getset(get = "pub")]
152 #[builder(default = "RtBuffer::new()")]
153 rtattrs: RtBuffer<Rta, Buffer>,
154}
155
156#[derive(Builder, Getters, Debug, Size, ToBytes, FromBytesWithInput, Header)]
158#[builder(pattern = "owned")]
159pub struct Ndmsg {
160 #[getset(get = "pub")]
162 ndm_family: RtAddrFamily,
163 #[builder(setter(skip))]
164 #[builder(default = "0")]
165 pad1: u8,
166 #[builder(setter(skip))]
167 #[builder(default = "0")]
168 pad2: u16,
169 #[getset(get = "pub")]
171 ndm_index: libc::c_int,
172 #[getset(get = "pub")]
174 ndm_state: Nud,
175 #[getset(get = "pub")]
177 #[builder(default = "Ntf::empty()")]
178 ndm_flags: Ntf,
179 #[getset(get = "pub")]
181 ndm_type: Rtn,
182 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
184 #[getset(get = "pub")]
185 #[builder(default = "RtBuffer::new()")]
186 rtattrs: RtBuffer<Nda, Buffer>,
187}
188
189#[derive(Builder, Getters, Debug, Size, ToBytes, FromBytes)]
191#[builder(pattern = "owned")]
192pub struct NdaCacheinfo {
193 #[getset(get = "pub")]
195 ndm_confirmed: u32,
196 #[getset(get = "pub")]
198 ndm_used: u32,
199 #[getset(get = "pub")]
201 ndm_updated: u32,
202 #[getset(get = "pub")]
204 ndm_refcnt: u32,
205}
206
207#[derive(Builder, Getters, Clone, Debug, Size, ToBytes, FromBytesWithInput, Header)]
209#[builder(pattern = "owned")]
210pub struct Tcmsg {
211 #[getset(get = "pub")]
213 tcm_family: libc::c_uchar,
214 #[builder(setter(skip))]
215 #[builder(default = "0")]
216 padding_char: libc::c_uchar,
217 #[builder(setter(skip))]
218 #[builder(default = "0")]
219 padding_short: libc::c_ushort,
220 #[getset(get = "pub")]
222 tcm_ifindex: libc::c_int,
223 #[getset(get = "pub")]
225 tcm_handle: u32,
226 #[getset(get = "pub")]
228 tcm_parent: u32,
229 #[getset(get = "pub")]
231 tcm_info: u32,
232 #[neli(input = "input.checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(input))?")]
234 #[getset(get = "pub")]
235 #[builder(default = "RtBuffer::new()")]
236 rtattrs: RtBuffer<Tca, Buffer>,
237}
238
239#[derive(Builder, Getters, Clone, Debug, Size, ToBytes, FromBytes, Header)]
241#[neli(header_bound = "T: RtaType")]
242#[neli(from_bytes_bound = "T: RtaType")]
243#[neli(from_bytes_bound = "P: FromBytesWithInput<Input = usize>")]
244#[neli(padding)]
245#[builder(pattern = "owned")]
246#[builder(build_fn(skip))]
247pub struct Rtattr<T, P> {
248 #[getset(get = "pub")]
250 #[builder(setter(skip))]
251 rta_len: libc::c_ushort,
252 #[getset(get = "pub")]
254 rta_type: T,
255 #[neli(
257 input = "(rta_len as usize).checked_sub(Self::header_size()).ok_or(DeError::InvalidInput(rta_len as usize))?"
258 )]
259 #[getset(get = "pub")]
260 rta_payload: P,
261}
262
263impl<T, P> RtattrBuilder<T, P>
264where
265 T: Size,
266 P: Size + ToBytes,
267{
268 pub fn build(self) -> Result<Rtattr<T, Buffer>, RtattrBuilderError> {
270 let rta_type = self
271 .rta_type
272 .ok_or_else(|| RtattrBuilderError::from(UninitializedFieldError::new("rta_type")))?;
273 let rta_payload = self
274 .rta_payload
275 .ok_or_else(|| RtattrBuilderError::from(UninitializedFieldError::new("rta_payload")))?;
276 let mut buffer = Cursor::new(vec![0; rta_payload.unpadded_size()]);
277 rta_payload.to_bytes(&mut buffer).map_err(|_| {
278 RtattrBuilderError::ValidationError(
279 "Could not convert payload to binary representation".to_string(),
280 )
281 })?;
282
283 let mut rtattr = Rtattr {
284 rta_len: 0,
285 rta_type,
286 rta_payload: Buffer::from(buffer.into_inner()),
287 };
288 rtattr.rta_len = rtattr.unpadded_size() as libc::c_ushort;
289 Ok(rtattr)
290 }
291}
292
293impl<T> Rtattr<T, Buffer>
294where
295 T: RtaType,
296{
297 pub fn nest<TT, P>(mut self, attr: &Rtattr<TT, P>) -> Result<Self, SerError>
301 where
302 TT: RtaType,
303 P: ToBytes,
304 {
305 self.add_nested_attribute(attr)?;
306 Ok(self)
307 }
308
309 fn add_nested_attribute<TT, P>(&mut self, attr: &Rtattr<TT, P>) -> Result<(), SerError>
311 where
312 TT: RtaType,
313 P: ToBytes,
314 {
315 let mut buffer = Cursor::new(Vec::new());
316 attr.to_bytes(&mut buffer)?;
317
318 self.rta_payload.extend_from_slice(buffer.get_ref());
319 self.rta_len += buffer.get_ref().len() as u16;
320 Ok(())
321 }
322
323 pub fn get_attr_handle<R>(&self) -> Result<RtAttrHandle<R>, DeError>
326 where
327 R: RtaType,
328 {
329 Ok(AttrHandle::new(RtBuffer::from_bytes_with_input(
330 &mut Cursor::new(self.rta_payload.as_ref()),
331 self.rta_payload.len(),
332 )?))
333 }
334}
335
336impl<T> Attribute<T> for Rtattr<T, Buffer>
337where
338 T: RtaType,
339{
340 fn payload(&self) -> &Buffer {
341 &self.rta_payload
342 }
343
344 fn set_payload<P>(&mut self, payload: &P) -> Result<(), SerError>
345 where
346 P: Size + ToBytes,
347 {
348 let mut buffer = Cursor::new(Vec::new());
349 payload.to_bytes(&mut buffer)?;
350
351 self.rta_len -= self.rta_payload.unpadded_size() as u16;
353 self.rta_len += buffer.get_ref().len() as u16;
354
355 self.rta_payload = Buffer::from(buffer.into_inner());
356
357 Ok(())
358 }
359}
360
361pub type RtAttrHandle<'a, T> = AttrHandle<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>;
363
364impl<'a, T> RtAttrHandle<'a, T>
365where
366 T: RtaType,
367{
368 pub fn get_nested_attributes<S>(&self, subattr: T) -> Result<RtAttrHandle<S>, DeError>
371 where
372 S: RtaType,
373 {
374 let payload = self
375 .get_attribute(subattr)
376 .ok_or_else(|| DeError::new("Couldn't find specified attribute"))?
377 .rta_payload
378 .as_ref();
379 Ok(AttrHandle::new(RtBuffer::from_bytes_with_input(
380 &mut Cursor::new(payload),
381 payload.len(),
382 )?))
383 }
384
385 pub fn get_attribute(&self, t: T) -> Option<&Rtattr<T, Buffer>> {
387 self.get_attrs().iter().find(|item| item.rta_type == t)
388 }
389
390 pub fn get_attr_payload_as<R>(&self, attr: T) -> Result<R, DeError>
392 where
393 R: FromBytes,
394 {
395 match self.get_attribute(attr) {
396 Some(a) => a.get_payload_as::<R>(),
397 _ => Err(DeError::new("Failed to find specified attribute")),
398 }
399 }
400
401 pub fn get_attr_payload_as_with_len<R>(&self, attr: T) -> Result<R, DeError>
403 where
404 R: FromBytesWithInput<Input = usize>,
405 {
406 match self.get_attribute(attr) {
407 Some(a) => a.get_payload_as_with_len::<R>(),
408 _ => Err(DeError::new("Failed to find specified attribute")),
409 }
410 }
411
412 pub fn get_attr_payload_as_with_len_borrowed<R>(&'a self, attr: T) -> Result<R, DeError>
414 where
415 R: FromBytesWithInputBorrowed<'a, Input = usize>,
416 {
417 match self.get_attribute(attr) {
418 Some(a) => a.get_payload_as_with_len_borrowed::<R>(),
419 _ => Err(DeError::new("Failed to find specified attribute")),
420 }
421 }
422}
423
424#[cfg(test)]
425mod test {
426 use super::*;
427
428 use std::net::Ipv4Addr;
429
430 use byteorder::{NativeEndian, WriteBytesExt};
431
432 use crate::{
433 consts::{nl::NlmF, socket::NlFamily},
434 err::RouterError,
435 nl::NlPayload,
436 router::synchronous::NlRouter,
437 test::setup,
438 utils::Groups,
439 };
440
441 #[test]
442 fn test_rta_deserialize() {
443 setup();
444
445 let mut buf = Cursor::new(vec![]);
446 buf.write_u16::<NativeEndian>(4).unwrap();
447 buf.write_u16::<NativeEndian>(0).unwrap();
448 buf.set_position(0);
449 Rtattr::<Rta, Buffer>::from_bytes(&mut buf).unwrap();
450 }
451
452 #[test]
453 fn test_rta_deserialize_err() {
454 setup();
455
456 let mut buf = Cursor::new(vec![]);
458 buf.write_u16::<NativeEndian>(3).unwrap();
459 buf.write_u16::<NativeEndian>(0).unwrap();
460 buf.set_position(0);
461 Rtattr::<Rta, Buffer>::from_bytes(&mut buf).unwrap_err();
462 }
463
464 #[test]
465 fn test_rtattr_padding() {
466 setup();
467
468 let attr = Rtattr {
469 rta_len: 5,
470 rta_type: Rta::Unspec,
471 rta_payload: vec![0u8],
472 };
473 let mut buffer = Cursor::new(Vec::new());
474 let buf_res = attr.to_bytes(&mut buffer);
475
476 buf_res.unwrap();
477 assert_eq!(buffer.into_inner().len(), 8);
479 }
480
481 #[test]
482 fn real_test_ifinfomsg() {
483 setup();
484
485 let (sock, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty()).unwrap();
486 sock.enable_strict_checking(true).unwrap();
487 let mut recv = sock
488 .send::<_, _, Rtm, Ifinfomsg>(
489 Rtm::Getlink,
490 NlmF::DUMP | NlmF::ACK,
491 NlPayload::Payload(
492 IfinfomsgBuilder::default()
493 .ifi_family(RtAddrFamily::Unspecified)
494 .build()
495 .unwrap(),
496 ),
497 )
498 .unwrap();
499 let all_msgs = recv
500 .try_fold(Vec::new(), |mut v, m| {
501 v.push(m?);
502 Result::<_, RouterError<Rtm, Ifinfomsg>>::Ok(v)
503 })
504 .unwrap();
505 let non_err_payloads = all_msgs.iter().fold(Vec::new(), |mut v, m| {
506 if let Some(p) = m.get_payload() {
507 v.push(p);
508 }
509 v
510 });
511 if non_err_payloads.is_empty() {
512 panic!("Only received done message and no additional information");
513 }
514 for payload in non_err_payloads {
515 let handle = payload.rtattrs.get_attr_handle();
516 handle
517 .get_attr_payload_as_with_len::<String>(Ifla::Ifname)
518 .unwrap();
519 if let Ok(attr) = handle.get_attr_payload_as_with_len::<Vec<u8>>(Ifla::Address) {
521 assert_eq!(attr.len(), 6);
522 }
523 }
524 }
525
526 #[test]
527 fn real_test_tcmsg() {
528 setup();
529
530 let (sock, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty()).unwrap();
531 sock.enable_strict_checking(true).unwrap();
532 let recv = sock
533 .send::<_, _, Rtm, Tcmsg>(
534 Rtm::Getqdisc,
535 NlmF::DUMP | NlmF::ACK,
536 NlPayload::Payload(
537 TcmsgBuilder::default()
538 .tcm_family(0)
539 .tcm_ifindex(0)
540 .tcm_handle(0)
541 .tcm_parent(0)
542 .tcm_info(0)
543 .build()
544 .unwrap(),
545 ),
546 )
547 .unwrap();
548 for msg in recv {
549 let msg = msg.unwrap();
550 assert!(matches!(msg.get_payload(), Some(Tcmsg { .. }) | None));
551 assert!(matches!(
552 msg.nl_type(),
553 Rtm::Newqdisc | Rtm::UnrecognizedConst(3)
554 ));
555 }
556 }
557
558 #[test]
559 #[cfg(target_env = "gnu")]
560 fn real_test_rtmsg_search() {
561 setup();
562
563 let dstip = Ipv4Addr::new(127, 0, 0, 1);
564 let raw_dstip = u32::from(dstip).to_be();
565 let route_attr = RtattrBuilder::default()
566 .rta_type(Rta::Dst)
567 .rta_payload(raw_dstip)
568 .build()
569 .unwrap();
570
571 let mut route_payload = RtBuffer::new();
572 route_payload.push(route_attr);
573
574 let (rtnl, _) = NlRouter::connect(NlFamily::Route, None, Groups::empty()).unwrap();
575
576 let ifroutemsg = RtmsgBuilder::default()
577 .rtm_family(RtAddrFamily::Inet)
578 .rtm_dst_len(32)
579 .rtm_src_len(0)
580 .rtm_tos(0)
581 .rtm_table(RtTable::Unspec)
582 .rtm_protocol(Rtprot::Unspec)
583 .rtm_scope(RtScope::Universe)
584 .rtm_type(Rtn::Unspec)
585 .rtm_flags(RtmF::from(libc::RTM_F_LOOKUP_TABLE))
586 .rtattrs(route_payload)
587 .build()
588 .unwrap();
589
590 let recv = rtnl
591 .send::<_, _, Rtm, Rtmsg>(Rtm::Getroute, NlmF::REQUEST, NlPayload::Payload(ifroutemsg))
592 .unwrap();
593
594 assert!(recv.count() > 0);
595 }
596}