use std::convert::TryFrom;
use std::mem;
use crate::{
attr::{AttrHandle, AttrHandleMut, Attribute},
consts::{alignto, rtnl::*},
err::{DeError, NlError, SerError},
parse::packet_length_u16,
types::{Buffer, DeBuffer, RtBuffer, SerBuffer},
utils::serialize,
Nl,
};
impl<T, P> Nl for RtBuffer<T, P>
where
T: RtaType,
P: Nl,
{
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
let mut pos = 0;
for item in self.iter() {
pos = drive_serialize!(item, mem, pos, asize);
}
drive_serialize!(END mem, pos);
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
let mut rtattrs = RtBuffer::new();
let mut pos = 0;
while pos < mem.len() {
let packet_len = packet_length_u16(mem, pos);
let (nlhdr, pos_tmp) = drive_deserialize!(
Rtattr<T, P>, mem, pos, alignto(packet_len)
);
rtattrs.push(nlhdr);
pos = pos_tmp;
}
drive_deserialize!(END mem, pos);
Ok(rtattrs)
}
fn size(&self) -> usize {
self.iter().fold(0, |acc, item| acc + item.asize())
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct Ifinfomsg {
pub ifi_family: RtAddrFamily,
padding: u8,
pub ifi_type: Arphrd,
pub ifi_index: libc::c_int,
pub ifi_flags: IffFlags,
pub ifi_change: Iff,
pub rtattrs: RtBuffer<Ifla, Buffer>,
}
impl Ifinfomsg {
pub fn new(
ifi_family: RtAddrFamily,
ifi_type: Arphrd,
ifi_index: libc::c_int,
ifi_flags: IffFlags,
ifi_change: Iff,
rtattrs: RtBuffer<Ifla, Buffer>,
) -> Self {
Ifinfomsg {
ifi_family,
padding: 0,
ifi_type,
ifi_index,
ifi_flags,
ifi_change,
rtattrs,
}
}
pub fn up(
ifi_family: RtAddrFamily,
ifi_type: Arphrd,
ifi_index: libc::c_int,
rtattrs: RtBuffer<Ifla, Buffer>,
) -> Self {
Ifinfomsg {
ifi_family,
padding: 0,
ifi_type,
ifi_index,
ifi_flags: IffFlags::new(&[Iff::Up]),
ifi_change: Iff::Up,
rtattrs,
}
}
pub fn down(
ifi_family: RtAddrFamily,
ifi_type: Arphrd,
ifi_index: libc::c_int,
rtattrs: RtBuffer<Ifla, Buffer>,
) -> Self {
Ifinfomsg {
ifi_family,
padding: 0,
ifi_type,
ifi_index,
ifi_flags: IffFlags::empty(),
ifi_change: Iff::Up,
rtattrs,
}
}
}
impl Nl for Ifinfomsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
mem;
self.ifi_family;
self.padding;
self.ifi_type;
self.ifi_change;
self.ifi_index;
self.ifi_flags;
self.ifi_change;
self.rtattrs, asize
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
mem;
Ifinfomsg {
ifi_family: RtAddrFamily,
padding: u8,
ifi_type: Arphrd,
ifi_index: libc::c_int,
ifi_flags: IffFlags,
ifi_change: Iff,
rtattrs: RtBuffer<Ifla, Buffer> => mem.len().checked_sub(
ifi_family.size()
+ padding.size()
+ ifi_type.size()
+ ifi_index.size()
+ ifi_flags.size()
+ ifi_change.size()
)
.ok_or(DeError::UnexpectedEOB)?
}
})
}
fn size(&self) -> usize {
self.ifi_family.size()
+ self.padding.size()
+ self.ifi_type.size()
+ self.ifi_index.size()
+ self.ifi_flags.size()
+ self.ifi_change.size()
+ self.rtattrs.size()
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct Ifaddrmsg {
pub ifa_family: RtAddrFamily,
pub ifa_prefixlen: libc::c_uchar,
pub ifa_flags: IfaFFlags,
pub ifa_scope: libc::c_uchar,
pub ifa_index: libc::c_int,
pub rtattrs: RtBuffer<Ifa, Buffer>,
}
impl Nl for Ifaddrmsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
let flags =
libc::c_uchar::try_from(&self.ifa_flags).map_err(|e| SerError::Msg(e.to_string()))?;
serialize! {
mem;
self.ifa_family;
self.ifa_prefixlen;
flags;
self.ifa_scope;
self.ifa_index;
self.rtattrs, asize
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
let pos = 0;
let (ifa_family, pos) = drive_deserialize!(RtAddrFamily, mem, pos);
let (ifa_prefixlen, pos) = drive_deserialize!(libc::c_uchar, mem, pos);
let (flags, pos) = drive_deserialize!(libc::c_uchar, mem, pos);
let (ifa_scope, pos) = drive_deserialize!(libc::c_uchar, mem, pos);
let (ifa_index, pos) = drive_deserialize!(libc::c_int, mem, pos);
let rtattrs_size = mem
.len()
.checked_sub(
ifa_family.size() + ifa_prefixlen.size() + 1 + ifa_scope.size() + ifa_index.size(),
)
.ok_or(DeError::UnexpectedEOB)?;
let (rtattrs, pos) = drive_deserialize!(RtBuffer<Ifa, Buffer>, mem, pos, rtattrs_size);
drive_deserialize!(END mem, pos);
Ok(Ifaddrmsg {
ifa_family,
ifa_prefixlen,
ifa_flags: IfaFFlags::from(flags),
ifa_scope,
ifa_index,
rtattrs,
})
}
fn size(&self) -> usize {
self.ifa_family.size()
+ self.ifa_prefixlen.size()
+ 1
+ self.ifa_scope.size()
+ self.ifa_index.size()
+ self.rtattrs.size()
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct Rtgenmsg {
pub rtgen_family: RtAddrFamily,
}
impl Nl for Rtgenmsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
self.rtgen_family.serialize(mem)
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(Self {
rtgen_family: RtAddrFamily::deserialize(mem)?,
})
}
fn size(&self) -> usize {
self.rtgen_family.size()
}
fn type_size() -> Option<usize> {
RtAddrFamily::type_size()
}
}
#[derive(Debug)]
pub struct Rtmsg {
pub rtm_family: RtAddrFamily,
pub rtm_dst_len: libc::c_uchar,
pub rtm_src_len: libc::c_uchar,
pub rtm_tos: libc::c_uchar,
pub rtm_table: RtTable,
pub rtm_protocol: Rtprot,
pub rtm_scope: RtScope,
pub rtm_type: Rtn,
pub rtm_flags: RtmFFlags,
pub rtattrs: RtBuffer<Rta, Buffer>,
}
impl Nl for Rtmsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
mem;
self.rtm_family;
self.rtm_dst_len;
self.rtm_src_len;
self.rtm_tos;
self.rtm_table;
self.rtm_protocol;
self.rtm_scope;
self.rtm_type;
self.rtm_flags;
self.rtattrs, asize
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
mem;
Rtmsg {
rtm_family: RtAddrFamily,
rtm_dst_len: libc::c_uchar,
rtm_src_len: libc::c_uchar,
rtm_tos: libc::c_uchar,
rtm_table: RtTable,
rtm_protocol: Rtprot,
rtm_scope: RtScope,
rtm_type: Rtn,
rtm_flags: RtmFFlags,
rtattrs: RtBuffer<Rta, Buffer> => mem.len().checked_sub(
rtm_family.size()
+ rtm_dst_len.size()
+ rtm_src_len.size()
+ rtm_tos.size()
+ rtm_table.size()
+ rtm_protocol.size()
+ rtm_scope.size()
+ rtm_type.size()
+ rtm_flags.size()
)
.ok_or(DeError::UnexpectedEOB)?
}
})
}
fn size(&self) -> usize {
self.rtm_family.size()
+ self.rtm_dst_len.size()
+ self.rtm_src_len.size()
+ self.rtm_tos.size()
+ self.rtm_table.size()
+ self.rtm_protocol.size()
+ self.rtm_scope.size()
+ self.rtm_type.size()
+ self.rtm_flags.size()
+ self.rtattrs.size()
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct Ndmsg {
pub ndm_family: RtAddrFamily,
pad1: u8,
pad2: u16,
pub ndm_index: libc::c_int,
pub ndm_state: NudFlags,
pub ndm_flags: NtfFlags,
pub ndm_type: Rtn,
pub rtattrs: RtBuffer<Nda, Buffer>,
}
impl Nl for Ndmsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
mem;
self.ndm_family;
self.pad1;
self.pad2;
self.ndm_index;
self.ndm_state;
self.ndm_flags;
self.ndm_type;
self.rtattrs, asize
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
mem;
Ndmsg {
ndm_family: RtAddrFamily,
pad1: u8,
pad2: u16,
ndm_index: libc::c_int,
ndm_state: NudFlags,
ndm_flags: NtfFlags,
ndm_type: Rtn,
rtattrs: RtBuffer<Nda, Buffer> => mem.len().checked_sub(
ndm_family.size()
+ pad1.size()
+ pad2.size()
+ ndm_index.size()
+ ndm_state.size()
+ ndm_flags.size()
+ ndm_type.size()
)
.ok_or(DeError::UnexpectedEOB)?
}
})
}
fn size(&self) -> usize {
self.ndm_family.size()
+ self.pad1.size()
+ self.pad2.size()
+ self.ndm_index.size()
+ self.ndm_state.size()
+ self.ndm_flags.size()
+ self.ndm_type.size()
+ self.rtattrs.asize()
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct NdaCacheinfo {
pub ndm_confirmed: u32,
pub ndm_used: u32,
pub ndm_updated: u32,
pub ndm_refcnt: u32,
}
impl Nl for NdaCacheinfo {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
mem;
self.ndm_confirmed;
self.ndm_used;
self.ndm_updated;
self.ndm_refcnt
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
mem;
NdaCacheinfo {
ndm_confirmed: u32,
ndm_used: u32,
ndm_updated: u32,
ndm_refcnt: u32
}
})
}
fn size(&self) -> usize {
self.ndm_confirmed.size()
+ self.ndm_used.size()
+ self.ndm_updated.size()
+ self.ndm_refcnt.size()
}
fn type_size() -> Option<usize> {
u32::type_size().map(|s| s * 4)
}
}
#[derive(Debug)]
pub struct Tcmsg {
pub tcm_family: libc::c_uchar,
padding_char: libc::c_uchar,
padding_short: libc::c_ushort,
pub tcm_ifindex: libc::c_int,
pub tcm_handle: u32,
pub tcm_parent: u32,
pub tcm_info: u32,
pub rtattrs: RtBuffer<Tca, Buffer>,
}
impl Nl for Tcmsg {
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
mem;
self.tcm_family;
self.padding_char;
self.padding_short;
self.tcm_ifindex;
self.tcm_handle;
self.tcm_parent;
self.tcm_info;
self.rtattrs, asize
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
mem;
Tcmsg {
tcm_family: libc::c_uchar,
padding_char: libc::c_uchar,
padding_short: libc::c_ushort,
tcm_ifindex: libc::c_int,
tcm_handle: u32,
tcm_parent: u32,
tcm_info: u32,
rtattrs: RtBuffer<Tca, Buffer> => mem.len().checked_sub(
tcm_family.size()
+ tcm_ifindex.size()
+ tcm_handle.size()
+ tcm_parent.size()
+ tcm_info.size()
)
.ok_or(DeError::UnexpectedEOB)?
}
})
}
fn size(&self) -> usize {
self.tcm_family.size()
+ mem::size_of::<libc::c_uchar>()
+ mem::size_of::<libc::c_ushort>()
+ self.tcm_ifindex.size()
+ self.padding_char.size()
+ self.padding_short.size()
+ self.tcm_handle.size()
+ self.tcm_parent.size()
+ self.tcm_info.size()
+ self.rtattrs.size()
}
fn type_size() -> Option<usize> {
None
}
}
#[derive(Debug)]
pub struct Rtattr<T, P> {
pub rta_len: libc::c_ushort,
pub rta_type: T,
pub rta_payload: P,
}
impl<T> Rtattr<T, Buffer>
where
T: RtaType,
{
pub fn new<P>(rta_len: Option<u16>, rta_type: T, rta_payload: P) -> Result<Self, NlError>
where
P: Nl,
{
let mut attr = Rtattr {
rta_len: rta_len.unwrap_or(0),
rta_type,
rta_payload: Buffer::new(),
};
attr.set_payload(&rta_payload).map_err(|e| {
NlError::new(format!("Failed to convert payload to a byte buffer: {}", e))
})?;
Ok(attr)
}
pub fn add_nested_attribute<TT, P>(&mut self, attr: &Rtattr<TT, P>) -> Result<(), NlError>
where
TT: RtaType,
P: Nl,
{
let ser_buffer = serialize(attr, true)?;
self.rta_payload.extend_from_slice(ser_buffer.as_ref());
self.rta_len += attr.asize() as u16;
Ok(())
}
pub fn get_attr_handle<R>(&self) -> Result<RtAttrHandle<R>, NlError>
where
R: RtaType,
{
Ok(AttrHandle::new(
RtBuffer::deserialize(self.rta_payload.as_ref()).map_err(NlError::new)?,
))
}
pub fn get_attr_handle_mut<R>(&mut self) -> Result<RtAttrHandleMut<R>, NlError>
where
R: RtaType,
{
Ok(AttrHandleMut::new(
RtBuffer::deserialize(self.rta_payload.as_ref()).map_err(NlError::new)?,
))
}
}
impl<T> Attribute<T> for Rtattr<T, Buffer>
where
T: RtaType,
{
fn payload(&self) -> &Buffer {
&self.rta_payload
}
fn set_payload<P>(&mut self, payload: &P) -> Result<(), NlError>
where
P: Nl,
{
let ser_buffer = serialize(payload, false)?;
self.rta_payload = Buffer::from(ser_buffer);
self.rta_len = (self.rta_len.size() + self.rta_type.size() + payload.size()) as u16;
Ok(())
}
}
impl<T, P> Nl for Rtattr<T, P>
where
T: RtaType,
P: Nl,
{
fn serialize(&self, mem: SerBuffer) -> Result<(), SerError> {
serialize! {
PAD self;
mem;
self.rta_len;
self.rta_type;
self.rta_payload
};
Ok(())
}
fn deserialize(mem: DeBuffer) -> Result<Self, DeError> {
Ok(deserialize! {
STRIP Self;
mem;
Rtattr<T, P> {
rta_len: libc::c_ushort,
rta_type: T,
rta_payload: P => (rta_len as usize).checked_sub(
rta_len.size() + rta_type.size()
)
.ok_or(DeError::UnexpectedEOB)?
} => alignto(rta_len as usize) - rta_len as usize
})
}
fn size(&self) -> usize {
self.rta_len.size() + self.rta_type.size() + self.rta_payload.size()
}
fn type_size() -> Option<usize> {
None
}
}
type RtAttrHandle<'a, T> = AttrHandle<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>;
type RtAttrHandleMut<'a, T> = AttrHandleMut<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>;
impl<'a, T> AttrHandle<'a, RtBuffer<T, Buffer>, Rtattr<T, Buffer>>
where
T: RtaType,
{
pub fn get_nested_attributes<S>(&mut self, subattr: T) -> Result<RtAttrHandle<S>, NlError>
where
S: RtaType,
{
Ok(AttrHandle::new(
RtBuffer::deserialize(
self.get_attribute(subattr)
.ok_or_else(|| NlError::new("Couldn't find specified attribute"))?
.rta_payload
.as_ref(),
)
.map_err(NlError::new)?,
))
}
pub fn get_attribute(&self, t: T) -> Option<&Rtattr<T, Buffer>> {
for item in self.get_attrs().iter() {
if item.rta_type == t {
return Some(&item);
}
}
None
}
pub fn get_attr_payload_as<R>(&self, attr: T) -> Result<R, NlError>
where
R: Nl,
{
match self.get_attribute(attr) {
Some(a) => a.get_payload_as::<R>(),
_ => Err(NlError::new("Failed to find specified attribute")),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{consts::rtnl::Rta, utils::serialize};
#[test]
fn test_rta_deserialize() {
let buf = &[4u8, 0, 0, 0] as &[u8];
assert!(Rtattr::<Rta, Buffer>::deserialize(buf).is_ok());
}
#[test]
fn test_rta_deserialize_err() {
let buf = &[3u8, 0, 0, 0] as &[u8];
assert!(Rtattr::<Rta, Buffer>::deserialize(buf).is_err());
}
#[test]
fn test_rtattr_padding() {
let attr = Rtattr {
rta_len: 5,
rta_type: Rta::Unspec,
rta_payload: vec![0u8],
};
let buf_res = serialize(&attr, true);
assert!(buf_res.is_ok());
assert_eq!(buf_res.unwrap().as_slice().len(), 8);
}
}